Compare commits
9
Commits
b6b5088107
...
75830b9204
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75830b9204 | ||
|
|
0498501b33 | ||
|
|
8c709a3f5f | ||
|
|
9ba6443f4f | ||
|
|
04e4efe1fc | ||
|
|
bce9675b0f | ||
|
|
f25108360b | ||
|
|
4ec02ac2bc | ||
|
|
97b357850c |
@@ -106,6 +106,10 @@
|
||||
android:theme="@style/Theme.DishMatch.NoSplash"
|
||||
android:screenOrientation="portrait"
|
||||
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
|
||||
<activity
|
||||
android:name="com.shuwei.dish.match.ui.SingleFragmentActivity"
|
||||
android:theme="@style/Theme.DishMatch.NoSplash"
|
||||
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
|
||||
<activity
|
||||
android:name="com.shuwei.dish.match.ui.FoodRecognizeActivity"
|
||||
android:theme="@style/Theme.DishMatch.NoSplash"
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.chad.library.adapter4.BaseQuickAdapter
|
||||
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
||||
@@ -25,7 +26,8 @@ import com.shuwei.dish.match.utils.ext.dp
|
||||
*/
|
||||
class Seasoning18GridAdapter(
|
||||
private val showNameOnly: Boolean = false,
|
||||
private val showAddress: Boolean = false
|
||||
private val showAddress: Boolean = false,
|
||||
private val leftRightSpace: Int = 10.dp
|
||||
) : BaseQuickAdapter<ScaleData, Seasoning18GridAdapter.VH>(mutableListOf()) {
|
||||
|
||||
/** 空位哨兵,address=0 表示无效 */
|
||||
@@ -47,12 +49,12 @@ class Seasoning18GridAdapter(
|
||||
val b = ListItemScale22RowBinding.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
|
||||
if (viewType == 0) it.setMargins(margin, 0, margin, 2.dp)
|
||||
else it.setMargins(margin, margin, margin, margin)
|
||||
val cellSize = (screenWidth - 6 * margin * 2 - leftRightSpace) / 6
|
||||
b.root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
width = cellSize
|
||||
height = cellSize
|
||||
if (viewType == 0) setMargins(margin, 0, margin, 2.dp)
|
||||
else setMargins(margin, margin, margin, margin)
|
||||
}
|
||||
val baseSp = (cellSize / parent.resources.displayMetrics.density).toInt()
|
||||
b.tvAddress.textSize = (baseSp * 0.14f).coerceIn(8f, 12f)
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.chad.library.adapter4.BaseQuickAdapter
|
||||
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
||||
@@ -51,7 +52,7 @@ class Seasoning22GridAdapter(
|
||||
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 {
|
||||
b.root.updateLayoutParams<FlexboxLayoutManager.LayoutParams> {
|
||||
width = cellSize
|
||||
height = cellSize
|
||||
setMargins(2.dp, 2.dp, 2.dp, 2.dp)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.shuwei.dish.match.adapter
|
||||
|
||||
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.ItemSeasoningWeightBinding
|
||||
|
||||
/**
|
||||
@@ -10,7 +12,7 @@ import com.shuwei.dish.match.databinding.ItemSeasoningWeightBinding
|
||||
* 用于 SubmitFoodActivity 展示各调料槽位的实时用量
|
||||
* 同名调料已在外部聚合,adapter 按 goodsName 作为唯一标识
|
||||
*/
|
||||
class SeasoningWeightAdapter : RecyclerView.Adapter<SeasoningWeightAdapter.VH>() {
|
||||
class SeasoningWeightAdapter : BaseQuickAdapter<SeasoningWeightAdapter.Item, SeasoningWeightAdapter.VH>() {
|
||||
|
||||
/** 调料展示数据项,key = goodsName(同名调料已聚合) */
|
||||
data class Item(
|
||||
@@ -20,19 +22,15 @@ class SeasoningWeightAdapter : RecyclerView.Adapter<SeasoningWeightAdapter.VH>()
|
||||
val useWeight: Double
|
||||
)
|
||||
|
||||
val items = mutableListOf<Item>()
|
||||
|
||||
/**
|
||||
* 更新单个 item,按 goodsName 匹配;不存在则追加
|
||||
*/
|
||||
fun updateItem(item: Item) {
|
||||
val idx = items.indexOfFirst { it.goodsName == item.goodsName }
|
||||
if (idx >= 0) {
|
||||
items[idx] = item
|
||||
notifyItemChanged(idx)
|
||||
set(idx, item)
|
||||
} else {
|
||||
items.add(item)
|
||||
notifyItemInserted(items.lastIndex)
|
||||
add(item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,29 +39,20 @@ class SeasoningWeightAdapter : RecyclerView.Adapter<SeasoningWeightAdapter.VH>()
|
||||
*/
|
||||
fun removeItem(goodsName: String) {
|
||||
val idx = items.indexOfFirst { it.goodsName == goodsName }
|
||||
if (idx >= 0) {
|
||||
items.removeAt(idx)
|
||||
notifyItemRemoved(idx)
|
||||
}
|
||||
if (idx >= 0) removeAt(idx)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
|
||||
val binding = ItemSeasoningWeightBinding.inflate(
|
||||
LayoutInflater.from(parent.context), parent, false
|
||||
)
|
||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
||||
val binding = ItemSeasoningWeightBinding.inflate(LayoutInflater.from(context), parent, false)
|
||||
return VH(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int) {
|
||||
holder.bind(items[position])
|
||||
override fun onBindViewHolder(holder: VH, position: Int, item: Item?) {
|
||||
item ?: return
|
||||
holder.binding.tvSeasoningName.text = item.goodsName
|
||||
holder.binding.tvSeasoningWeight.text = "${item.useWeight}g"
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
inner class VH(var binding: ItemSeasoningWeightBinding) : QuickViewHolder(binding.root)
|
||||
|
||||
class VH(private val b: ItemSeasoningWeightBinding) : RecyclerView.ViewHolder(b.root) {
|
||||
fun bind(item: Item) {
|
||||
b.tvSeasoningName.text = item.goodsName
|
||||
b.tvSeasoningWeight.text = "${item.useWeight}g"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,9 @@ open class BaseActivity : AppCompatActivity() {
|
||||
)
|
||||
}
|
||||
|
||||
/** 返回 Fragment 容器的 View ID,供子类加载 Fragment 使用 */
|
||||
protected fun getFragmentContainerId(): Int = R.id.flContainer
|
||||
|
||||
override fun setContentView(view: View?) {
|
||||
if (view == binding.root) {
|
||||
super.setContentView(view)
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.shuwei.dish.match.db
|
||||
|
||||
import com.shuwei.dish.match.base.BaseApp
|
||||
import com.shuwei.dish.match.db.dao.AppDao
|
||||
import com.shuwei.dish.match.db.dao.SeasoningSlotDao
|
||||
import com.shuwei.dish.match.entity.CookFoodEntity
|
||||
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
|
||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
||||
@@ -9,7 +10,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
||||
class AppRepository(val appDao: AppDao) {
|
||||
class AppRepository(val appDao: AppDao, val seasoningSlotDao: SeasoningSlotDao) {
|
||||
|
||||
suspend fun insertCookFood(item: CookFoodEntity) = withContext(Dispatchers.IO) {
|
||||
appDao.insertCookFood(item)
|
||||
@@ -105,6 +106,15 @@ class AppRepository(val appDao: AppDao) {
|
||||
suspend fun insertSeasoning(item: SeasoningEntity) = withContext(Dispatchers.IO) {
|
||||
appDao.insertSeasoning(item)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定设备是否已配置调料槽位
|
||||
* @param deviceId 目标设备 ID
|
||||
* @return true 表示已有配置,false 表示未配置
|
||||
*/
|
||||
suspend fun hasSeasoningSlotConfig(deviceId: String) = withContext(Dispatchers.IO) {
|
||||
seasoningSlotDao.queryByDeviceId(deviceId).isNotEmpty()
|
||||
}
|
||||
}
|
||||
//
|
||||
//class SeasoningRepository(val seasoningDao: SeasoningDao) {
|
||||
|
||||
@@ -26,8 +26,10 @@ open class CommonDialog(
|
||||
private var contentText: String? = null
|
||||
private var negativeText = "取消"
|
||||
private var positiveText = "确认"
|
||||
private var neutralText: String? = null
|
||||
private var negativeClick: (() -> Unit)? = null
|
||||
private var positiveClick: (() -> Unit)? = null
|
||||
private var neutralClick: (() -> Unit)? = null
|
||||
private var dismissCallback: (() -> Unit)? = null
|
||||
|
||||
/** 设置标题,为空时隐藏 */
|
||||
@@ -36,18 +38,24 @@ open class CommonDialog(
|
||||
/** 设置内容,为空时隐藏 */
|
||||
fun setContent(text: String): CommonDialog = apply { contentText = text }
|
||||
|
||||
/** 设置左侧取消按钮文字及点击回调 */
|
||||
/** 设置左侧取消按钮文字及点击回调(3按钮模式下对应顶部按钮) */
|
||||
fun setNegativeButton(text: String, onClick: (() -> Unit)? = null): CommonDialog = apply {
|
||||
negativeText = text
|
||||
negativeClick = onClick
|
||||
}
|
||||
|
||||
/** 设置右侧确认按钮文字及点击回调 */
|
||||
/** 设置右侧确认按钮文字及点击回调(3按钮模式下对应底部按钮) */
|
||||
fun setPositiveButton(text: String, onClick: (() -> Unit)? = null): CommonDialog = apply {
|
||||
positiveText = text
|
||||
positiveClick = onClick
|
||||
}
|
||||
|
||||
/** 设置中间第三个按钮文字及点击回调,调用后自动切换为垂直3按钮布局 */
|
||||
fun setNeutralButton(text: String, onClick: (() -> Unit)? = null): CommonDialog = apply {
|
||||
neutralText = text
|
||||
neutralClick = onClick
|
||||
}
|
||||
|
||||
/** 设置弹窗消失回调 */
|
||||
fun setOnDismissCallback(callback: () -> Unit): CommonDialog = apply {
|
||||
dismissCallback = callback
|
||||
@@ -70,6 +78,8 @@ open class CommonDialog(
|
||||
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 }
|
||||
|
||||
// 2按钮点击事件
|
||||
binding.btnLeft.setOnClickListener {
|
||||
negativeClick?.invoke()
|
||||
dismiss()
|
||||
@@ -78,6 +88,21 @@ open class CommonDialog(
|
||||
positiveClick?.invoke()
|
||||
dismiss()
|
||||
}
|
||||
|
||||
// 3按钮点击事件(negativeClick/positiveClick 与2按钮模式共用)
|
||||
binding.btnTop.setOnClickListener {
|
||||
negativeClick?.invoke()
|
||||
dismiss()
|
||||
}
|
||||
binding.btnMiddle.setOnClickListener {
|
||||
neutralClick?.invoke()
|
||||
dismiss()
|
||||
}
|
||||
binding.btnBottom.setOnClickListener {
|
||||
positiveClick?.invoke()
|
||||
dismiss()
|
||||
}
|
||||
|
||||
setOnDismissListener { dismissCallback?.invoke() }
|
||||
}
|
||||
|
||||
@@ -87,10 +112,23 @@ open class CommonDialog(
|
||||
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
|
||||
|
||||
val hasThreeButtons = !neutralText.isNullOrEmpty()
|
||||
binding.layoutButtonsHorizontal.visibility = if (hasThreeButtons) View.GONE else View.VISIBLE
|
||||
binding.layoutButtonsVertical.visibility = if (hasThreeButtons) View.VISIBLE else View.GONE
|
||||
|
||||
if (hasThreeButtons) {
|
||||
// 3按钮模式:顶部=取消,中间=中立,底部=确认
|
||||
binding.btnTop.text = negativeText
|
||||
binding.btnMiddle.text = neutralText
|
||||
binding.btnBottom.text = positiveText
|
||||
} else {
|
||||
// 2按钮模式
|
||||
binding.btnLeft.text = negativeText
|
||||
binding.btnRight.text = positiveText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class SafeDialog(activity: Activity, @StyleRes themeResId: Int) :
|
||||
Dialog(activity, themeResId) {
|
||||
|
||||
@@ -47,6 +47,7 @@ import java.net.UnknownHostException
|
||||
* @param onSuccess 成功回调
|
||||
* @param onFailure 自定义错误回调(可选,不传则用默认提示)
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
suspend fun <T> request(
|
||||
onRequest: suspend () -> ApiResponse<T>,
|
||||
onSuccess: (T) -> Unit,
|
||||
@@ -73,6 +74,7 @@ data class CodeMsg(
|
||||
var msg: String? = null
|
||||
)
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
fun getApiException(e: Exception): ApiException {
|
||||
// 捕获各类网络异常并转换为自定义异常
|
||||
return when (e) {
|
||||
@@ -116,7 +118,7 @@ fun httpException2ApiException(e: HttpException): ApiException {
|
||||
val body = e.response()?.errorBody()?.string()
|
||||
val codeMsg: CodeMsg? = try {
|
||||
body?.toObject<CodeMsg>()
|
||||
} catch (ex: Exception) {
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
return ApiException(
|
||||
|
||||
@@ -8,104 +8,89 @@ import com.shuwei.dish.match.entity.FoodRecord
|
||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class NetViewModel : ViewModel() {
|
||||
/**
|
||||
* 网络请求 ViewModel,所有网络操作通过 RemoteRepository 发起,不直接依赖 apiService
|
||||
* @param repository 网络数据仓库,默认使用单例实例
|
||||
*/
|
||||
class NetViewModel(
|
||||
private val repository: RemoteRepository = RemoteRepository()
|
||||
) : ViewModel() {
|
||||
|
||||
/**
|
||||
* 提交制作菜品
|
||||
*/
|
||||
fun submitCookFood(
|
||||
entity: CookFoodEntity,
|
||||
onSuccess: (Any?) -> Unit,
|
||||
onFailure: (String, String) -> Unit
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
request(
|
||||
onRequest = {
|
||||
apiService.submitCookFood(param = entity)
|
||||
},
|
||||
onSuccess = onSuccess,
|
||||
onFailure = onFailure
|
||||
)
|
||||
repository.submitCookFood(entity, onSuccess, onFailure)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询菜品详情
|
||||
*/
|
||||
fun getFoodDetail(
|
||||
foodId: String,
|
||||
onSuccess: (CookFoodEntity?) -> Unit,
|
||||
onFailure: (String, String) -> Unit
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
request(
|
||||
onRequest = {
|
||||
apiService.getFoodDetail(foodId = foodId)
|
||||
},
|
||||
onSuccess = onSuccess,
|
||||
onFailure = onFailure
|
||||
)
|
||||
repository.getFoodDetail(foodId, onSuccess, onFailure)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 搜索菜品列表
|
||||
*/
|
||||
fun searchFoodList(
|
||||
param: MutableMap<String, Any>,
|
||||
onSuccess: (MutableList<FoodRecord>?) -> Unit,
|
||||
onFailure: (String, String) -> Unit
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
request(
|
||||
onRequest = {
|
||||
apiService.searchFoodList(param = param)
|
||||
},
|
||||
onSuccess = onSuccess,
|
||||
onFailure = onFailure
|
||||
)
|
||||
repository.searchFoodList(param, onSuccess, onFailure)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询采样数据列表
|
||||
*/
|
||||
fun getSamplingList(
|
||||
param: MutableMap<String, Any>,
|
||||
onSuccess: (MutableList<FoodRecord>?) -> Unit,
|
||||
onFailure: (String, String) -> Unit
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
request(
|
||||
onRequest = {
|
||||
apiService.getSamplingList(param = param)
|
||||
},
|
||||
onSuccess = onSuccess,
|
||||
onFailure = onFailure
|
||||
)
|
||||
repository.getSamplingList(param, onSuccess, onFailure)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询物品信息列表
|
||||
*/
|
||||
fun queryGoodsList(
|
||||
param: MutableMap<String, Any>,
|
||||
onSuccess: (MutableList<CookFoodGoodsEntity>?) -> Unit,
|
||||
onFailure: (String, String) -> Unit
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
request(
|
||||
onRequest = {
|
||||
apiService.queryGoodsList(param = param)
|
||||
},
|
||||
onSuccess = onSuccess,
|
||||
onFailure = onFailure
|
||||
)
|
||||
repository.queryGoodsList(param, onSuccess, onFailure)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询调料信息列表
|
||||
*/
|
||||
fun querySeasoningList(
|
||||
param: MutableMap<String, Any>,
|
||||
onSuccess: (MutableList<SeasoningEntity>?) -> Unit,
|
||||
onFailure: (String, String) -> Unit
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
request(
|
||||
onRequest = {
|
||||
apiService.querySeasoningList(param = param)
|
||||
},
|
||||
onSuccess = onSuccess,
|
||||
onFailure = onFailure
|
||||
)
|
||||
repository.querySeasoningList(param, onSuccess, onFailure)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.shuwei.dish.match.net
|
||||
|
||||
import com.shuwei.dish.match.entity.CookFoodEntity
|
||||
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
|
||||
import com.shuwei.dish.match.entity.FoodRecord
|
||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
||||
|
||||
/**
|
||||
* 网络数据仓库,封装所有 ApiService 调用
|
||||
* ViewModel 不直接接触 apiService,统一通过此类发起网络请求
|
||||
*/
|
||||
class RemoteRepository {
|
||||
|
||||
/**
|
||||
* 提交制作菜品
|
||||
* @param entity 菜品实体
|
||||
* @param onSuccess 成功回调
|
||||
* @param onFailure 失败回调,参数为 (errorCode, errorMsg)
|
||||
*/
|
||||
suspend fun submitCookFood(
|
||||
entity: CookFoodEntity,
|
||||
onSuccess: (Any?) -> Unit,
|
||||
onFailure: (String, String) -> Unit
|
||||
) = request(
|
||||
onRequest = { apiService.submitCookFood(param = entity) },
|
||||
onSuccess = onSuccess,
|
||||
onFailure = onFailure
|
||||
)
|
||||
|
||||
/**
|
||||
* 查询菜品详情
|
||||
* @param foodId 菜品 ID
|
||||
* @param onSuccess 成功回调
|
||||
* @param onFailure 失败回调,参数为 (errorCode, errorMsg)
|
||||
*/
|
||||
suspend fun getFoodDetail(
|
||||
foodId: String,
|
||||
onSuccess: (CookFoodEntity?) -> Unit,
|
||||
onFailure: (String, String) -> Unit
|
||||
) = request(
|
||||
onRequest = { apiService.getFoodDetail(foodId = foodId) },
|
||||
onSuccess = onSuccess,
|
||||
onFailure = onFailure
|
||||
)
|
||||
|
||||
/**
|
||||
* 搜索菜品列表
|
||||
* @param param 查询参数
|
||||
* @param onSuccess 成功回调
|
||||
* @param onFailure 失败回调,参数为 (errorCode, errorMsg)
|
||||
*/
|
||||
suspend fun searchFoodList(
|
||||
param: MutableMap<String, Any>,
|
||||
onSuccess: (MutableList<FoodRecord>?) -> Unit,
|
||||
onFailure: (String, String) -> Unit
|
||||
) = request(
|
||||
onRequest = { apiService.searchFoodList(param = param) },
|
||||
onSuccess = onSuccess,
|
||||
onFailure = onFailure
|
||||
)
|
||||
|
||||
/**
|
||||
* 查询采样数据列表
|
||||
* @param param 查询参数
|
||||
* @param onSuccess 成功回调
|
||||
* @param onFailure 失败回调,参数为 (errorCode, errorMsg)
|
||||
*/
|
||||
suspend fun getSamplingList(
|
||||
param: MutableMap<String, Any>,
|
||||
onSuccess: (MutableList<FoodRecord>?) -> Unit,
|
||||
onFailure: (String, String) -> Unit
|
||||
) = request(
|
||||
onRequest = { apiService.getSamplingList(param = param) },
|
||||
onSuccess = onSuccess,
|
||||
onFailure = onFailure
|
||||
)
|
||||
|
||||
/**
|
||||
* 查询物品信息列表
|
||||
* @param param 查询参数
|
||||
* @param onSuccess 成功回调
|
||||
* @param onFailure 失败回调,参数为 (errorCode, errorMsg)
|
||||
*/
|
||||
suspend fun queryGoodsList(
|
||||
param: MutableMap<String, Any>,
|
||||
onSuccess: (MutableList<CookFoodGoodsEntity>?) -> Unit,
|
||||
onFailure: (String, String) -> Unit
|
||||
) = request(
|
||||
onRequest = { apiService.queryGoodsList(param = param) },
|
||||
onSuccess = onSuccess,
|
||||
onFailure = onFailure
|
||||
)
|
||||
|
||||
/**
|
||||
* 查询调料信息列表
|
||||
* @param param 查询参数
|
||||
* @param onSuccess 成功回调
|
||||
* @param onFailure 失败回调,参数为 (errorCode, errorMsg)
|
||||
*/
|
||||
suspend fun querySeasoningList(
|
||||
param: MutableMap<String, Any>,
|
||||
onSuccess: (MutableList<SeasoningEntity>?) -> Unit,
|
||||
onFailure: (String, String) -> Unit
|
||||
) = request(
|
||||
onRequest = { apiService.querySeasoningList(param = param) },
|
||||
onSuccess = onSuccess,
|
||||
onFailure = onFailure
|
||||
)
|
||||
}
|
||||
@@ -102,7 +102,7 @@ class DishSamplingActivity : BaseActivity() {
|
||||
private fun initViewModel() {
|
||||
val db = BaseApp.instance!!.database
|
||||
val factory =
|
||||
AppFactory(AppRepository(db.appDao()))
|
||||
AppFactory(AppRepository(db.appDao(), db.seasoningSlotDao()))
|
||||
appViewModel =
|
||||
ViewModelProvider(this, factory)[AppViewModel::class.java]
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ class HomeActivity : BaseActivity() {
|
||||
|
||||
private fun initViewModel() {
|
||||
val db = BaseApp.instance!!.database
|
||||
val factory = AppFactory(AppRepository(db.appDao()))
|
||||
val factory = AppFactory(AppRepository(db.appDao(), db.seasoningSlotDao()))
|
||||
appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ class InitActivity : BaseActivity() {
|
||||
|
||||
private fun initViewModel() {
|
||||
val db = BaseApp.instance!!.database
|
||||
val factory = AppFactory(AppRepository(db.appDao()))
|
||||
val factory = AppFactory(AppRepository(db.appDao(), db.seasoningSlotDao()))
|
||||
appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.shuwei.dish.match.scale.ScaleServiceManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import com.shuwei.dish.match.utils.NetworkUtil
|
||||
import com.shuwei.dish.match.utils.SizeTool
|
||||
import com.shuwei.dish.match.utils.WeightUtil
|
||||
import com.shuwei.dish.match.utils.ext.dp
|
||||
import com.shuwei.dish.match.utils.ext.toast
|
||||
@@ -51,7 +52,7 @@ private const val VIEW_TYPE_SCALE1 = 3
|
||||
class MasterScaleActivity : BaseActivity() {
|
||||
|
||||
private lateinit var binding: ActivityMasterScaleBinding
|
||||
|
||||
private val size by lazy { SizeTool.getFlexLayoutSize(10.dp) }
|
||||
private data class DeviceGroup(
|
||||
val deviceId: String,
|
||||
val ip: String,
|
||||
@@ -357,12 +358,7 @@ class MasterScaleActivity : BaseActivity() {
|
||||
b.tvDeviceLabel.text = "设备:${group.deviceId}"
|
||||
|
||||
if (innerAdapter == null) {
|
||||
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
|
||||
b.rvScales.layoutParams = b.rvScales.layoutParams.also { it.height = rvHeight }
|
||||
b.rvScales.layoutParams = b.rvScales.layoutParams.also { it.height = size.rvHeight }
|
||||
b.rvScales.layoutManager = FlexboxLayoutManager(b.root.context).apply {
|
||||
flexDirection = FlexDirection.COLUMN
|
||||
flexWrap = FlexWrap.WRAP
|
||||
@@ -370,8 +366,8 @@ class MasterScaleActivity : BaseActivity() {
|
||||
}
|
||||
b.rvScales.itemAnimator = null
|
||||
innerAdapter = Seasoning22GridAdapter(
|
||||
largeSize,
|
||||
smallSize,
|
||||
size.largeSize,
|
||||
size.smallSize,
|
||||
showNameOnly = false,
|
||||
showAddress = true
|
||||
).apply {
|
||||
@@ -455,6 +451,7 @@ class MasterScaleActivity : BaseActivity() {
|
||||
val scale = scales.firstOrNull()
|
||||
b.llScaleContainer.tag = scale
|
||||
b.tvAddr.text = scale?.let { "秤 ${it.address}" } ?: ""
|
||||
b.tvName.text = scale?.name?.takeIf { it.isNotBlank() } ?: "-"
|
||||
b.tvWeight.text = scale?.let { "${it.weight} g" } ?: ""
|
||||
b.tvState.text = stateStr(scale?.state)
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ class SamplingListActivity : BaseActivity() {
|
||||
|
||||
private fun initViewModel() {
|
||||
val db = BaseApp.instance!!.database
|
||||
val factory = AppFactory(AppRepository(db.appDao()))
|
||||
val factory = AppFactory(AppRepository(db.appDao(), db.seasoningSlotDao()))
|
||||
appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class SelectDishActivity : BaseActivity() {
|
||||
|
||||
private fun initViewModel() {
|
||||
val db = BaseApp.instance!!.database
|
||||
val factory = AppFactory(AppRepository(db.appDao()))
|
||||
val factory = AppFactory(AppRepository(db.appDao(), db.seasoningSlotDao()))
|
||||
appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
|
||||
}
|
||||
|
||||
|
||||
@@ -2,33 +2,20 @@ package com.shuwei.dish.match.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
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 android.provider.Settings
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.toColorInt
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.camera.view.PreviewView
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import com.shuwei.dish.match.R
|
||||
import com.shuwei.dish.match.base.BaseActivity
|
||||
import com.shuwei.dish.match.base.DeviceRole
|
||||
import com.shuwei.dish.match.base.GlobalData
|
||||
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.dialog.CommonDialog
|
||||
import com.shuwei.dish.match.utils.SpTool
|
||||
import com.shuwei.dish.match.utils.ext.dp
|
||||
import com.shuwei.dish.match.utils.ext.clickWithDebounce
|
||||
import com.shuwei.dish.match.utils.ext.gone
|
||||
import com.shuwei.dish.match.utils.ext.startActivity
|
||||
import com.shuwei.dish.match.utils.ext.toast
|
||||
import com.shuwei.dish.match.utils.ext.visible
|
||||
|
||||
/**
|
||||
@@ -42,10 +29,10 @@ class SettingActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
private lateinit var binding: ActivitySettingBinding
|
||||
private val cameraUtils: CameraUtils by lazy {
|
||||
CameraUtils(this)
|
||||
}
|
||||
private lateinit var previewView: PreviewView
|
||||
// private val cameraUtils: CameraUtils by lazy {
|
||||
// CameraUtils(this)
|
||||
// }
|
||||
// private lateinit var previewView: PreviewView
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -55,104 +42,54 @@ class SettingActivity : BaseActivity() {
|
||||
setTitleBar(titleBarAction = {
|
||||
it.visible()
|
||||
}, titleAction = {
|
||||
it.text = "设置"
|
||||
it.text = "设备设置"
|
||||
}, rightIconActon = {
|
||||
it.gone()
|
||||
})
|
||||
|
||||
// 默认显示设备配置Fragment
|
||||
if (savedInstanceState == null) {
|
||||
showDeviceConfigFragment()
|
||||
}
|
||||
|
||||
initUI()
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化UI和事件监听
|
||||
*/
|
||||
private fun initUI() {
|
||||
// 设备配置按钮点击事件
|
||||
binding.btnDeviceConfig.setOnClickListener {
|
||||
showDeviceConfigFragment()
|
||||
|
||||
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()
|
||||
|
||||
loadTabStyle(button = binding.btnDeviceConfig, isSelected = false)
|
||||
loadTabStyle(button = binding.btnSeasoningConfig, isSelected = false)
|
||||
loadTabStyle(button = binding.btnFoodCollect, isSelected = true)
|
||||
}
|
||||
|
||||
// 主设备显示「秤数据监控」入口
|
||||
if (GlobalData.deviceRole == DeviceRole.MASTER) {
|
||||
binding.btnScaleMonitor.visible()
|
||||
binding.btnScaleMonitor.setOnClickListener {
|
||||
it.visible()
|
||||
it.clickWithDebounce {
|
||||
startActivity<MasterScaleActivity>()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 初始化按钮颜色(默认设备配置为选中状态)
|
||||
initView()
|
||||
|
||||
loadTabStyle(button = binding.btnDeviceConfig, isSelected = true)
|
||||
loadTabStyle(button = binding.btnSeasoningConfig, isSelected = false)
|
||||
loadTabStyle(button = binding.btnFoodCollect, isSelected = false)
|
||||
}
|
||||
|
||||
// /** 刷新角色按钮文字 */
|
||||
// private fun updateRoleButtonText() {
|
||||
// val roleLabel = if (GlobalData.deviceRole == DeviceRole.MASTER) "主设备" else "子设备"
|
||||
// binding.btnDeviceRole.text = "设备角色:$roleLabel"
|
||||
// // 默认显示设备配置Fragment
|
||||
// if (savedInstanceState == null) {
|
||||
// showDeviceConfigFragment()
|
||||
// }
|
||||
//
|
||||
// initUI()
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载顶部Tab按钮样式
|
||||
*
|
||||
* @param button 按钮
|
||||
* @param isSelected 是否选中
|
||||
*/
|
||||
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)
|
||||
} else {
|
||||
// 未选中
|
||||
setTextColor("#5E7585".toColorInt())
|
||||
textSize = 26f
|
||||
setTypeface(null, android.graphics.Typeface.NORMAL)
|
||||
background = null
|
||||
|
||||
private fun initView() {
|
||||
binding.tvCurrentMode.text = when (SpTool.cookMode) {
|
||||
0 -> "当前:制作模式"
|
||||
1 -> "当前:采样模式"
|
||||
else -> "当前:未知模式"
|
||||
}
|
||||
binding.llCookMode.clickWithDebounce {
|
||||
SingleFragmentActivity.start(this, SingleFragmentActivity.PageType.COOK_MODE)
|
||||
}
|
||||
binding.llSeasoningConfig.clickWithDebounce {
|
||||
SingleFragmentActivity.start(this, SingleFragmentActivity.PageType.SEASONING_CONFIG)
|
||||
}
|
||||
binding.llFoodCollect.clickWithDebounce {
|
||||
checkCameraPermission()
|
||||
}
|
||||
}
|
||||
|
||||
private fun openCollectPage() {
|
||||
SingleFragmentActivity.start(this, SingleFragmentActivity.PageType.FOOD_COLLECT)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查相机权限并显示 CollectFragment
|
||||
*/
|
||||
@SuppressLint("ObsoleteSdkInt")
|
||||
private fun checkCameraPermissionAndShowFragment() {
|
||||
val collectFragment = fragmentList[2]
|
||||
private fun checkCameraPermission() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
// Android 6.0 以下,直接显示 Fragment
|
||||
showFragment(collectFragment)
|
||||
// Android 6.0 以下,直接打开
|
||||
openCollectPage()
|
||||
return
|
||||
}
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
@@ -160,109 +97,228 @@ class SettingActivity : BaseActivity() {
|
||||
Manifest.permission.CAMERA
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
// 权限已授予,直接显示 Fragment
|
||||
showFragment(collectFragment)
|
||||
// 权限已授予,直接打开
|
||||
openCollectPage()
|
||||
} else {
|
||||
// 权限未授予,申请权限
|
||||
requestPermission(Manifest.permission.CAMERA) { isGranted ->
|
||||
if (isGranted) {
|
||||
// 权限已授予,显示 CollectFragment
|
||||
showFragment(collectFragment)
|
||||
// 权限已授予,直接打开
|
||||
openCollectPage()
|
||||
} else {
|
||||
// 权限被拒绝
|
||||
toast("暂无相机权限,无法使用菜品采集功能")
|
||||
showNotGrantedCameraPermissionDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化相机
|
||||
* @param container 相机预览容器
|
||||
*/
|
||||
fun initCamera(container: ViewGroup) {
|
||||
cameraUtils.initCamera()
|
||||
val previewBinding =
|
||||
LayoutCameraPreviewBinding.inflate(layoutInflater, container)
|
||||
previewView = previewBinding.previewView.also {
|
||||
it.updateLayoutParams {
|
||||
width = 456.dp
|
||||
height = 342.dp
|
||||
private fun showNotGrantedCameraPermissionDialog() {
|
||||
CommonDialog(this)
|
||||
.setTitle("温馨提示")
|
||||
.setContent("暂无相机权限,无法使用菜品采集功能,请确认是否同意开始权限?")
|
||||
.setNegativeButton("不同意")
|
||||
.setPositiveButton("同意") {
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||
data = Uri.fromParts("package", packageName, null)
|
||||
}
|
||||
startActivity(intent) {
|
||||
checkCameraPermission()
|
||||
}
|
||||
}
|
||||
cameraUtils.setPreviewController(previewView)
|
||||
.show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Activity 恢复时绑定相机
|
||||
*/
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
cameraUtils.bind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Activity 暂停时解绑相机
|
||||
*/
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
cameraUtils.unbind()
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供给 Fragment 调用的拍照方法
|
||||
*/
|
||||
fun takePhoto(succCallback: (Uri) -> Unit, failCallback: (msg: String) -> Unit = {}) {
|
||||
cameraUtils.takePhoto(succCallback = succCallback, failCallback = failCallback)
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示设备切换Fragment
|
||||
*/
|
||||
private fun showDeviceConfigFragment() {
|
||||
showFragment(fragmentList[0])
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示设调料配置Fragment
|
||||
*/
|
||||
private fun showSeasoningConfigFragment() {
|
||||
showFragment(fragmentList[1])
|
||||
}
|
||||
|
||||
private val fragmentList = mutableListOf<Fragment>().apply {
|
||||
add(DeviceConfigFragment())
|
||||
add(SeasoningConfigFragment())
|
||||
add(CollectFragment())
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示指定的 Fragment
|
||||
* 使用 add/show/hide 方式管理 Fragment,避免重复创建
|
||||
* 已添加的 Fragment 直接显示,未添加的 Fragment 先添加再显示
|
||||
*/
|
||||
private fun showFragment(fragment: Fragment) {
|
||||
runCatching {
|
||||
supportFragmentManager.beginTransaction().apply {
|
||||
// 如果 Fragment 还未添加到容器中,则添加
|
||||
if (!fragment.isAdded) {
|
||||
add(binding.fragmentContainer.id, fragment)
|
||||
} else {
|
||||
// 已添加过,直接显示
|
||||
show(fragment)
|
||||
}
|
||||
|
||||
// 隐藏其他 Fragment
|
||||
fragmentList.forEach { otherFragment ->
|
||||
if (otherFragment != fragment && otherFragment.isAdded) {
|
||||
hide(otherFragment)
|
||||
}
|
||||
}
|
||||
|
||||
commit()
|
||||
}
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
}
|
||||
//
|
||||
// /**
|
||||
// * 初始化UI和事件监听
|
||||
// */
|
||||
// private fun initUI() {
|
||||
// // 设备配置按钮点击事件
|
||||
// binding.btnDeviceConfig.setOnClickListener {
|
||||
// showDeviceConfigFragment()
|
||||
//
|
||||
// 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()
|
||||
//
|
||||
// loadTabStyle(button = binding.btnDeviceConfig, isSelected = false)
|
||||
// loadTabStyle(button = binding.btnSeasoningConfig, isSelected = false)
|
||||
// loadTabStyle(button = binding.btnFoodCollect, isSelected = true)
|
||||
// }
|
||||
//
|
||||
// // 主设备显示「秤数据监控」入口
|
||||
// if (GlobalData.deviceRole == DeviceRole.MASTER) {
|
||||
// binding.btnScaleMonitor.visible()
|
||||
// binding.btnScaleMonitor.setOnClickListener {
|
||||
// startActivity<MasterScaleActivity>()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 初始化按钮颜色(默认设备配置为选中状态)
|
||||
//
|
||||
// loadTabStyle(button = binding.btnDeviceConfig, isSelected = true)
|
||||
// loadTabStyle(button = binding.btnSeasoningConfig, isSelected = false)
|
||||
// loadTabStyle(button = binding.btnFoodCollect, isSelected = false)
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 加载顶部Tab按钮样式
|
||||
// *
|
||||
// * @param button 按钮
|
||||
// * @param isSelected 是否选中
|
||||
// */
|
||||
// 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)
|
||||
// } else {
|
||||
// // 未选中
|
||||
// setTextColor("#5E7585".toColorInt())
|
||||
// textSize = 26f
|
||||
// setTypeface(null, android.graphics.Typeface.NORMAL)
|
||||
// background = null
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 检查相机权限并显示 CollectFragment
|
||||
// */
|
||||
// @SuppressLint("ObsoleteSdkInt")
|
||||
// private fun checkCameraPermissionAndShowFragment() {
|
||||
// val collectFragment = fragmentList[2]
|
||||
// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
// // Android 6.0 以下,直接显示 Fragment
|
||||
// showFragment(collectFragment)
|
||||
// return
|
||||
// }
|
||||
// if (ContextCompat.checkSelfPermission(
|
||||
// this,
|
||||
// Manifest.permission.CAMERA
|
||||
// ) == PackageManager.PERMISSION_GRANTED
|
||||
// ) {
|
||||
// // 权限已授予,直接显示 Fragment
|
||||
// showFragment(collectFragment)
|
||||
// } else {
|
||||
// // 权限未授予,申请权限
|
||||
// requestPermission(Manifest.permission.CAMERA) { isGranted ->
|
||||
// if (isGranted) {
|
||||
// // 权限已授予,显示 CollectFragment
|
||||
// showFragment(collectFragment)
|
||||
// } else {
|
||||
// // 权限被拒绝
|
||||
// toast("暂无相机权限,无法使用菜品采集功能")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 初始化相机
|
||||
// * @param container 相机预览容器
|
||||
// */
|
||||
// fun initCamera(container: ViewGroup) {
|
||||
// cameraUtils.initCamera()
|
||||
// val previewBinding =
|
||||
// LayoutCameraPreviewBinding.inflate(layoutInflater, container)
|
||||
// previewView = previewBinding.previewView.also {
|
||||
// it.updateLayoutParams {
|
||||
// width = 456.dp
|
||||
// height = 342.dp
|
||||
// }
|
||||
// }
|
||||
// cameraUtils.setPreviewController(previewView)
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Activity 恢复时绑定相机
|
||||
// */
|
||||
// override fun onResume() {
|
||||
// super.onResume()
|
||||
// cameraUtils.bind()
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Activity 暂停时解绑相机
|
||||
// */
|
||||
// override fun onPause() {
|
||||
// super.onPause()
|
||||
// cameraUtils.unbind()
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 提供给 Fragment 调用的拍照方法
|
||||
// */
|
||||
// fun takePhoto(succCallback: (Uri) -> Unit, failCallback: (msg: String) -> Unit = {}) {
|
||||
// cameraUtils.takePhoto(succCallback = succCallback, failCallback = failCallback)
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 显示设备切换Fragment
|
||||
// */
|
||||
// private fun showDeviceConfigFragment() {
|
||||
// showFragment(fragmentList[0])
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 显示设调料配置Fragment
|
||||
// */
|
||||
// private fun showSeasoningConfigFragment() {
|
||||
// showFragment(fragmentList[1])
|
||||
// }
|
||||
//
|
||||
// private val fragmentList = mutableListOf<Fragment>().apply {
|
||||
// add(DeviceConfigFragment())
|
||||
// add(SeasoningConfigFragment())
|
||||
// add(CollectFragment())
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 显示指定的 Fragment
|
||||
// * 使用 add/show/hide 方式管理 Fragment,避免重复创建
|
||||
// * 已添加的 Fragment 直接显示,未添加的 Fragment 先添加再显示
|
||||
// */
|
||||
// private fun showFragment(fragment: Fragment) {
|
||||
// runCatching {
|
||||
// supportFragmentManager.beginTransaction().apply {
|
||||
// // 如果 Fragment 还未添加到容器中,则添加
|
||||
// if (!fragment.isAdded) {
|
||||
// add(binding.fragmentContainer.id, fragment)
|
||||
// } else {
|
||||
// // 已添加过,直接显示
|
||||
// show(fragment)
|
||||
// }
|
||||
//
|
||||
// // 隐藏其他 Fragment
|
||||
// fragmentList.forEach { otherFragment ->
|
||||
// if (otherFragment != fragment && otherFragment.isAdded) {
|
||||
// hide(otherFragment)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// commit()
|
||||
// }
|
||||
// }.onFailure {
|
||||
// it.printStackTrace()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.shuwei.dish.match.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.shuwei.dish.match.base.BaseActivity
|
||||
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.ext.gone
|
||||
import com.shuwei.dish.match.utils.ext.visible
|
||||
|
||||
/**
|
||||
* 通用单 Fragment 容器 Activity
|
||||
* 通过 [PageType] 决定加载哪个 Fragment,避免为每个配置/功能页面单独创建 Activity
|
||||
*/
|
||||
class SingleFragmentActivity : BaseActivity() {
|
||||
|
||||
interface LifecycleCallback {
|
||||
fun onActivityResume()
|
||||
fun onActivityPause()
|
||||
}
|
||||
|
||||
/**
|
||||
* 支持的页面类型枚举
|
||||
* 新增页面时在此追加枚举值,并在 [createFragment] 中补充对应分支
|
||||
*/
|
||||
enum class PageType {
|
||||
/** 菜品采集页 */
|
||||
FOOD_COLLECT,
|
||||
|
||||
/** 设备配置页 */
|
||||
COOK_MODE,
|
||||
|
||||
/** 调料配置页 */
|
||||
SEASONING_CONFIG,
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val EXTRA_PAGE_TYPE = "extra_page_type"
|
||||
|
||||
/**
|
||||
* 启动 SingleFragmentActivity
|
||||
* @param context 调用方 Context
|
||||
* @param pageType 要显示的页面类型
|
||||
*/
|
||||
fun start(context: Context, pageType: PageType) {
|
||||
Intent(context, SingleFragmentActivity::class.java).apply {
|
||||
putExtra(EXTRA_PAGE_TYPE, pageType.name)
|
||||
context.startActivity(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var lifecycleCallback: LifecycleCallback? = null
|
||||
fun setLifecycleCallback(callback: LifecycleCallback) {
|
||||
lifecycleCallback = callback
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHeaderBackground()
|
||||
|
||||
// 解析页面类型,非法值直接关闭
|
||||
val typeName = intent.getStringExtra(EXTRA_PAGE_TYPE)
|
||||
val pageType = typeName?.let {
|
||||
runCatching { PageType.valueOf(it) }.getOrNull()
|
||||
}
|
||||
if (pageType == null) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
// 配置标题栏
|
||||
setTitleBar(
|
||||
titleBarAction = { it.visible() },
|
||||
titleAction = { it.text = getTitleForPage(pageType) },
|
||||
rightIconActon = { it.gone() }
|
||||
)
|
||||
|
||||
// 首次创建时加载 Fragment,避免屏幕旋转重复添加
|
||||
if (savedInstanceState == null) {
|
||||
val fragment = createFragment(pageType)
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(getFragmentContainerId(), fragment)
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据页面类型返回对应标题文本
|
||||
*/
|
||||
private fun getTitleForPage(pageType: PageType): String = when (pageType) {
|
||||
PageType.FOOD_COLLECT -> "食材采样"
|
||||
PageType.COOK_MODE -> "餐品模式"
|
||||
PageType.SEASONING_CONFIG -> "调料区设置"
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据页面类型创建对应 Fragment 实例
|
||||
*/
|
||||
private fun createFragment(pageType: PageType): Fragment = when (pageType) {
|
||||
PageType.FOOD_COLLECT -> CollectFragment()
|
||||
PageType.COOK_MODE -> DeviceConfigFragment()
|
||||
PageType.SEASONING_CONFIG -> SeasoningConfigFragment()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
lifecycleCallback?.onActivityResume()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
lifecycleCallback?.onActivityPause()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,6 +27,7 @@ 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.SizeTool
|
||||
import com.shuwei.dish.match.utils.WeightUtil
|
||||
import com.shuwei.dish.match.utils.ext.dp
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -61,6 +62,7 @@ class SlaveActivity : BaseActivity() {
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
/** 当前高亮清除任务,新高亮触发时取消旧任务 */
|
||||
private var clearHighlightRunnable: Runnable? = null
|
||||
private val size by lazy { SizeTool.getFlexLayoutSize(10.dp) }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -90,13 +92,8 @@ class SlaveActivity : BaseActivity() {
|
||||
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
|
||||
height = size.rvHeight
|
||||
weight = 0f
|
||||
topMargin = 20.dp
|
||||
}
|
||||
@@ -105,7 +102,7 @@ class SlaveActivity : BaseActivity() {
|
||||
flexWrap = FlexWrap.WRAP
|
||||
alignItems = AlignItems.FLEX_START
|
||||
}
|
||||
scale22Adapter = Seasoning22GridAdapter(largeSize, smallSize, showNameOnly = false).also {
|
||||
scale22Adapter = Seasoning22GridAdapter(size.largeSize, size.smallSize, showNameOnly = false).also {
|
||||
it.onItemClick = { scale, _ -> showTareDialog(scale.address) }
|
||||
}
|
||||
binding.rvScaleList.adapter = scale22Adapter
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.shuwei.dish.match.ui
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.addCallback
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
@@ -13,6 +14,7 @@ import com.shuwei.dish.match.base.BaseActivity
|
||||
import com.shuwei.dish.match.base.BaseApp
|
||||
import com.shuwei.dish.match.databinding.ActivitySubmitFoodBinding
|
||||
import com.shuwei.dish.match.db.AppRepository
|
||||
import com.shuwei.dish.match.dialog.CommonDialog
|
||||
import com.shuwei.dish.match.entity.CookFoodEntity
|
||||
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
|
||||
import com.shuwei.dish.match.entity.FoodRecord
|
||||
@@ -23,6 +25,7 @@ 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.roundedDecimalPlace
|
||||
import com.shuwei.dish.match.utils.ext.roundedOneDecimalPlace
|
||||
import com.shuwei.dish.match.utils.ext.startActivity
|
||||
import com.shuwei.dish.match.utils.ext.toJsonString
|
||||
@@ -83,6 +86,12 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
*/
|
||||
private val rawWeightMap = mutableMapOf<String, Triple<String, String, Double>>()
|
||||
|
||||
/**
|
||||
* isCooking=true 时从 goodsList 预填充的历史用量,key = goodsName,value = goodsId to useWeight
|
||||
* 用于 refreshAdapterByName 时叠加到秤新增用量上
|
||||
*/
|
||||
private val baseSeasoningMap = mutableMapOf<String, Pair<String, Double>>()
|
||||
|
||||
@Suppress("unchecked_cast", "DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -93,8 +102,11 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
intent.extras?.apply {
|
||||
food = getSerializable(FOOD_ITEM) as FoodRecord?
|
||||
Log.d(TAG, "onCreate: cookMode=${food?.cookMode}")
|
||||
// 非烹饪中,从菜品主辅材配置页面带入数据
|
||||
// isCooking=false:从前一页面带入主辅材数据;isCooking=true:从数据库查询
|
||||
if (food?.isCooking == false) {
|
||||
goodsList = getSerializable(GOODS_LIST) as MutableList<CookFoodGoodsEntity>?
|
||||
Log.d(TAG, "onCreate: goodsList=${goodsList.toJsonString()}")
|
||||
}
|
||||
}
|
||||
setTitleBar(titleBarAction = {
|
||||
it.visible()
|
||||
@@ -102,11 +114,17 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
it.text = food?.foodName
|
||||
}, rightIconActon = {
|
||||
it.gone()
|
||||
it.setImageResource(R.drawable.ic_setting)
|
||||
}, backAction = {
|
||||
it.setOnClickListener {
|
||||
remindSaveDataDialog()
|
||||
}
|
||||
})
|
||||
setupRecyclerView()
|
||||
addViewListener()
|
||||
loadSlotsThenObserveScales()
|
||||
onBackPressedDispatcher.addCallback(this) {
|
||||
remindSaveDataDialog()
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 RecyclerView,2列 GridLayoutManager */
|
||||
@@ -118,7 +136,7 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
|
||||
/**
|
||||
* 从 Room 加载槽位配置后,开始订阅子设备秤数据
|
||||
* 确保 slotMap 就绪后再处理秤数据,避免名称为空
|
||||
* isCooking=true 时先从数据库查询 goodsList,筛选调料预填充 adapter,再开始观测
|
||||
*/
|
||||
private fun loadSlotsThenObserveScales() {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
@@ -127,8 +145,30 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
slots.forEach { slot ->
|
||||
slotMap["${slot.deviceId}#${slot.address}"] = slot.goodsId to slot.goodsName
|
||||
}
|
||||
if (food?.isCooking == true) {
|
||||
// 制作中:从数据库查询 goodsList,筛选调料数据预填充 adapter
|
||||
val foodId = food?.foodId ?: return@withContext
|
||||
val cookMode = food?.cookMode ?: return@withContext
|
||||
appViewModel.getCookFoodGoodsList(foodId, cookMode) { list ->
|
||||
goodsList = list
|
||||
list.filter { it.materialType == 3 }.forEach { item ->
|
||||
val name = item.goodsName ?: ""
|
||||
val weight = item.useWeight ?: 0.0
|
||||
baseSeasoningMap[name] = (item.goodsId) to weight
|
||||
seasoningAdapter.updateItem(
|
||||
SeasoningWeightAdapter.Item(
|
||||
goodsId = item.goodsId,
|
||||
goodsName = name,
|
||||
useWeight = weight
|
||||
)
|
||||
)
|
||||
}
|
||||
observeScaleData()
|
||||
}
|
||||
} else {
|
||||
observeScaleData()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,33 +214,34 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 goodsName 聚合 rawWeightMap,将同名调料的用量累加后推给 adapter
|
||||
* 聚合后总量 < 0.5g 则从 adapter 移除
|
||||
* 按 goodsName 聚合 rawWeightMap,叠加历史用量后推给 adapter
|
||||
* isCooking=true 时只新增,不移除(历史调料数据不可减少)
|
||||
*/
|
||||
private fun refreshAdapterByName(goodsName: String) {
|
||||
val grouped = rawWeightMap.values.filter { it.second == goodsName }
|
||||
if (grouped.isEmpty()) {
|
||||
val newWeight = grouped.sumOf { it.third }.roundedOneDecimalPlace()
|
||||
val (baseGoodsId, baseWeight) = baseSeasoningMap[goodsName] ?: ("" to 0.0)
|
||||
val totalWeight = (newWeight + baseWeight).roundedOneDecimalPlace()
|
||||
if (totalWeight < 0.5) {
|
||||
// 制作中模式:已有历史数据,不允许移除
|
||||
if (food?.isCooking != true) {
|
||||
seasoningAdapter.removeItem(goodsName)
|
||||
}
|
||||
return
|
||||
}
|
||||
val totalWeight = grouped.sumOf { it.third }.roundedOneDecimalPlace()
|
||||
if (totalWeight < 0.5) {
|
||||
seasoningAdapter.removeItem(goodsName)
|
||||
} else {
|
||||
val goodsId = grouped.firstOrNull()?.first?.takeIf { it.isNotEmpty() } ?: baseGoodsId
|
||||
seasoningAdapter.updateItem(
|
||||
SeasoningWeightAdapter.Item(
|
||||
goodsId = grouped.first().first,
|
||||
goodsId = goodsId,
|
||||
goodsName = goodsName,
|
||||
useWeight = totalWeight
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addViewListener() {
|
||||
|
||||
binding.btnCook.clickWithDebounce { cook() }
|
||||
|
||||
binding.btnWeightClear.clickWithDebounce { WeightUtil.tareTwo(2) }
|
||||
binding.btnSubmit.clickWithDebounce {
|
||||
if (cookFoodEntity.foodWeight <= 0.toDouble()) {
|
||||
toast("未识别到菜品熟重")
|
||||
@@ -212,8 +253,14 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
// 监听主设备 2格秤的熟重
|
||||
WeightUtil.addWeightListener(TAG) { address, _, weight ->
|
||||
if (address == AddressUtil.TWO) {
|
||||
cookFoodEntity.foodWeight = weight.toDouble()
|
||||
binding.tvTotalWeight.text = "${weight / 1000f}"
|
||||
cookFoodEntity.foodWeight = weight
|
||||
if (weight > 1000) {
|
||||
binding.tvTotalWeight.text = "${(weight / 1000f).roundedDecimalPlace(3)}"
|
||||
binding.tvWeightUnit.text = "千克"
|
||||
} else {
|
||||
binding.tvTotalWeight.text = "$weight"
|
||||
binding.tvWeightUnit.text = "克"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,7 +278,7 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
showLoading()
|
||||
|
||||
// 将调料数据转为 CookFoodGoodsEntity 追加到 goodsList
|
||||
val typeToken = object : TypeToken<List<CookFoodGoodsEntity>>() {}
|
||||
// val typeToken = object : TypeToken<List<CookFoodGoodsEntity>>() {}
|
||||
val tempSeasoningList = seasoningData.map { item ->
|
||||
CookFoodGoodsEntity().also { entity ->
|
||||
entity.goodsId = item.goodsId
|
||||
@@ -348,7 +395,7 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
|
||||
private fun initViewModel() {
|
||||
val db = BaseApp.instance!!.database
|
||||
val factory = AppFactory(AppRepository(db.appDao()))
|
||||
val factory = AppFactory(AppRepository(db.appDao(), db.seasoningSlotDao()))
|
||||
appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
|
||||
}
|
||||
|
||||
@@ -356,4 +403,18 @@ class SubmitFoodActivity : BaseActivity() {
|
||||
WeightUtil.removeWeightListener(TAG)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
/**
|
||||
* 未保存数据提醒dialog
|
||||
*/
|
||||
private fun remindSaveDataDialog() {
|
||||
CommonDialog(this)
|
||||
.setTitle("温馨提示")
|
||||
.setContent("请确认是否存在未保存的数据?")
|
||||
.setNegativeButton("取消,留在页面")
|
||||
.setNeutralButton("返回上页,不保存") { finish() }
|
||||
.setPositiveButton("保存为烹饪中菜品") { cook() }
|
||||
.show()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,20 +10,26 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.camera.view.PreviewView
|
||||
import androidx.core.view.doOnLayout
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.shuwei.dish.match.R
|
||||
import com.shuwei.dish.match.adapter.FoodCollectionAdapter
|
||||
import com.shuwei.dish.match.base.BaseFragment
|
||||
import com.shuwei.dish.match.databinding.FragmentCollectBinding
|
||||
import com.shuwei.dish.match.databinding.LayoutCameraPreviewBinding
|
||||
import com.shuwei.dish.match.dialog.Loading
|
||||
import com.shuwei.dish.match.objbox.FoodCollectionBean
|
||||
import com.shuwei.dish.match.objbox.FoodModule
|
||||
import com.shuwei.dish.match.ui.SettingActivity
|
||||
import com.shuwei.dish.match.ui.SingleFragmentActivity
|
||||
import com.shuwei.dish.match.utils.BitmapSaver
|
||||
import com.shuwei.dish.match.utils.CameraUtils
|
||||
import com.shuwei.dish.match.utils.Debouncer
|
||||
import com.shuwei.dish.match.utils.ImageUtil
|
||||
import com.shuwei.dish.match.utils.LogSaveUtil
|
||||
import com.shuwei.dish.match.utils.ext.clickWithDebounce
|
||||
import com.shuwei.dish.match.utils.ext.dp
|
||||
import com.shuwei.dish.match.utils.ext.toast
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@@ -31,7 +37,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "CollectFragment"
|
||||
const val MAX_COUNT = 9
|
||||
const val MAX_COUNT = 18
|
||||
}
|
||||
|
||||
private var selectedFoodId: String? = ""
|
||||
@@ -58,7 +64,8 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
// }
|
||||
// }
|
||||
private val debouncer = Debouncer(2000)
|
||||
private lateinit var settingActivity: SettingActivity
|
||||
// private lateinit var settingActivity: SettingActivity
|
||||
private lateinit var currentActivity: SingleFragmentActivity
|
||||
|
||||
private val collectionAdapter: FoodCollectionAdapter by lazy {
|
||||
FoodCollectionAdapter(foodCollectionList).apply {
|
||||
@@ -168,7 +175,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
return
|
||||
}
|
||||
showWaitingDialog("采集中……")
|
||||
settingActivity.takePhoto(succCallback = cameraCallback, failCallback = { errMsg ->
|
||||
cameraUtils.takePhoto(succCallback = cameraCallback, failCallback = { errMsg ->
|
||||
hideWaitingDialog()
|
||||
// toast("拍照异常,请重新操作")
|
||||
// if (cameraErrorCount >= 10) {
|
||||
@@ -183,9 +190,22 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
}
|
||||
|
||||
override fun initialize() {
|
||||
settingActivity = activity as SettingActivity
|
||||
currentActivity = activity as SingleFragmentActivity
|
||||
// 首次显示 CollectFragment 时初始化相机
|
||||
settingActivity.initCamera(binding.flCameraPreview)
|
||||
initCamera(binding.flCameraPreview)
|
||||
currentActivity.setLifecycleCallback(object : SingleFragmentActivity.LifecycleCallback {
|
||||
override fun onActivityResume() {
|
||||
// 首次进入时 previewView 尚未 attach,由 doOnLayout 负责首次 bind
|
||||
// 从后台切回时 previewView 已 attach,直接 bind 恢复预览
|
||||
if (::previewView.isInitialized && previewView.isAttachedToWindow) {
|
||||
cameraUtils.bind()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityPause() {
|
||||
cameraUtils.unbind()
|
||||
}
|
||||
})
|
||||
|
||||
binding.rvFoodList.let {
|
||||
it.layoutManager =
|
||||
@@ -340,7 +360,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (isVisible.not()) return
|
||||
// 不使用 isVisible 判断,首次打开时 View 尚未 attach 到 Window,isVisible 会返回 false 导致遮罩永不隐藏
|
||||
binding.llCameraFlag.run {
|
||||
visibility = View.VISIBLE
|
||||
postDelayed({
|
||||
@@ -361,7 +381,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
|
||||
private fun searchByFoodName(name: String, block: (String) -> Unit) {}
|
||||
private fun hideWaitingDialog() {
|
||||
settingActivity.dismissLoading()
|
||||
currentActivity.dismissLoading()
|
||||
}
|
||||
|
||||
private fun showWaitingDialog(msg: String) {
|
||||
@@ -369,6 +389,34 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
Loading.updateContent(msg)
|
||||
return
|
||||
}
|
||||
settingActivity.showLoading(msg)
|
||||
currentActivity.showLoading(msg)
|
||||
}
|
||||
|
||||
private lateinit var previewView: PreviewView
|
||||
|
||||
private val cameraUtils: CameraUtils by lazy {
|
||||
CameraUtils(requireActivity())
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化相机
|
||||
* @param container 相机预览容器
|
||||
*/
|
||||
fun initCamera(container: ViewGroup) {
|
||||
cameraUtils.initCamera()
|
||||
val previewBinding =
|
||||
LayoutCameraPreviewBinding.inflate(layoutInflater, container)
|
||||
previewView = previewBinding.previewView.also {
|
||||
it.updateLayoutParams {
|
||||
width = 456.dp
|
||||
height = 342.dp
|
||||
}
|
||||
}
|
||||
cameraUtils.setPreviewController(previewView)
|
||||
// doOnLayout 确保 previewView 测量完成(有有效尺寸和 Surface)后再 bind,解决首次打开无预览问题
|
||||
previewView.doOnLayout {
|
||||
cameraUtils.bind()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +1,22 @@
|
||||
package com.shuwei.dish.match.ui.fragment
|
||||
|
||||
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.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.graphics.toColorInt
|
||||
import androidx.core.view.forEach
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
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.SeasoningSearchDialog
|
||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
||||
import com.shuwei.dish.match.entity.ResetReasoningRecord
|
||||
import com.shuwei.dish.match.adapter.TextCellAdapter
|
||||
import com.shuwei.dish.match.base.BaseActivity
|
||||
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.clickWithDebounce
|
||||
import com.shuwei.dish.match.utils.ext.gone
|
||||
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.ui.SamplingListActivity
|
||||
import com.shuwei.dish.match.ui.SelectDishActivity
|
||||
import com.shuwei.dish.match.utils.SpTool
|
||||
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
|
||||
|
||||
/**
|
||||
@@ -65,10 +41,10 @@ class DeviceConfigFragment : BaseFragment<FragmentDeviceConfigBinding>() {
|
||||
|
||||
// private lateinit var appViewModel: AppViewModel
|
||||
|
||||
private val weightArray = SparseArray<Double>()
|
||||
private val addressArray = AddressUtil.getWeighAddressArray()
|
||||
private var seasoningItems = mutableListOf<SeasoningEntity>()
|
||||
private val detector = MultiClickDetector(targetCount = 10, intervalMs = 800)
|
||||
// private val weightArray = SparseArray<Double>()
|
||||
// private val addressArray = AddressUtil.getWeighAddressArray()
|
||||
// private var seasoningItems = mutableListOf<SeasoningEntity>()
|
||||
// private val detector = MultiClickDetector(targetCount = 10, intervalMs = 800)
|
||||
|
||||
override fun inflateBinding(
|
||||
inflater: LayoutInflater,
|
||||
@@ -140,7 +116,8 @@ class DeviceConfigFragment : BaseFragment<FragmentDeviceConfigBinding>() {
|
||||
loadCookSpan()
|
||||
loadSamplingSpan(isClicked = true)
|
||||
activity?.startActivity<SamplingListActivity> {
|
||||
putExtra(SamplingListActivity.IS_CONFIG_PAGE, true) }
|
||||
putExtra(SamplingListActivity.IS_CONFIG_PAGE, true)
|
||||
}
|
||||
activity?.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,14 @@ 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.entity.SeasoningEntity
|
||||
import com.shuwei.dish.match.entity.SeasoningSlotEntity
|
||||
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.scale.ScaleEvent.SlotConfig
|
||||
import com.shuwei.dish.match.ui.SettingActivity
|
||||
import com.shuwei.dish.match.ui.SingleFragmentActivity
|
||||
import com.shuwei.dish.match.utils.SizeTool
|
||||
import com.shuwei.dish.match.utils.WeightUtil
|
||||
import com.shuwei.dish.match.utils.ext.dp
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -35,8 +37,10 @@ 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
|
||||
}
|
||||
@@ -46,21 +50,28 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
container: ViewGroup?
|
||||
) = FragmentSeasoningConfigBinding.inflate(inflater, container, false)
|
||||
|
||||
private lateinit var settingActivity: SettingActivity
|
||||
private lateinit var currentActivity: SingleFragmentActivity
|
||||
// private lateinit var appViewModel: AppViewModel
|
||||
|
||||
private val adapter22 by lazy { buildAdapter22() }
|
||||
private val adapter18 by lazy { Seasoning18GridAdapter(showNameOnly = true) }
|
||||
private val adapter18 by lazy {
|
||||
Seasoning18GridAdapter(
|
||||
showNameOnly = true,
|
||||
leftRightSpace = 20.dp
|
||||
)
|
||||
}
|
||||
|
||||
/** 各秤的上一次重量快照,key 格式 "{deviceId}#{address}",用于计算 delta */
|
||||
private val weightSnapshot = mutableMapOf<String, Double>()
|
||||
|
||||
/** 主线程 Handler,用于延迟清除高亮 */
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
/** 当前高亮清除任务,新高亮触发时取消旧任务 */
|
||||
private var clearHighlightRunnable: Runnable? = null
|
||||
|
||||
override fun initialize() {
|
||||
settingActivity = activity as SettingActivity
|
||||
currentActivity = activity as SingleFragmentActivity
|
||||
// initViewModel()
|
||||
setupRecyclerViews()
|
||||
bindAdapterClicks()
|
||||
@@ -72,10 +83,7 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
/** 绑定油盆格子点击,弹出调料选择弹窗并保存到 DEVICE_ID_1 */
|
||||
private fun bindOilSlotClick() {
|
||||
binding.tvOilSlot.setOnClickListener {
|
||||
SeasoningSelectDialog(
|
||||
activity = settingActivity,
|
||||
clickIndex = 0,
|
||||
onItemSelected = { item ->
|
||||
openSeasoningSelectDialog(0) { item ->
|
||||
binding.tvOilSlot.text = item.goodsName ?: "-"
|
||||
saveSlotAndBroadcast(
|
||||
SeasoningSlotEntity(
|
||||
@@ -86,17 +94,13 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
)
|
||||
)
|
||||
}
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
/** 绑定两个 Adapter 的格子点击,弹出调料选择弹窗 */
|
||||
private fun bindAdapterClicks() {
|
||||
adapter22.onItemClick = { scaleData, position ->
|
||||
SeasoningSelectDialog(
|
||||
activity = settingActivity,
|
||||
clickIndex = position,
|
||||
onItemSelected = { item ->
|
||||
openSeasoningSelectDialog(position) { item ->
|
||||
adapter22.updateItemName(position, item.goodsName ?: "")
|
||||
saveSlotAndBroadcast(
|
||||
SeasoningSlotEntity(
|
||||
@@ -107,13 +111,9 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
)
|
||||
)
|
||||
}
|
||||
).show()
|
||||
}
|
||||
adapter18.onItemClick = { scaleData, position ->
|
||||
SeasoningSelectDialog(
|
||||
activity = settingActivity,
|
||||
clickIndex = position,
|
||||
onItemSelected = { item ->
|
||||
openSeasoningSelectDialog(position) { item ->
|
||||
adapter18.updateItemName(position, item.goodsName ?: "")
|
||||
saveSlotAndBroadcast(
|
||||
SeasoningSlotEntity(
|
||||
@@ -124,10 +124,26 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
)
|
||||
)
|
||||
}
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开调料选择弹窗
|
||||
*
|
||||
* @param position 格子位置
|
||||
* @param callback 调料选择回调
|
||||
*/
|
||||
private fun openSeasoningSelectDialog(
|
||||
position: Int,
|
||||
callback: (item: SeasoningEntity) -> Unit
|
||||
) {
|
||||
SeasoningSelectDialog(
|
||||
activity = currentActivity,
|
||||
clickIndex = position,
|
||||
onItemSelected = callback
|
||||
).show()
|
||||
}
|
||||
|
||||
/**
|
||||
* 将单个槽位写入 Room,再读取全量配置广播给子设备
|
||||
* 仅主设备执行广播,子设备不调用此方法
|
||||
@@ -150,24 +166,24 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
// appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
|
||||
// }
|
||||
|
||||
|
||||
private val size by lazy { SizeTool.getFlexLayoutSize(20.dp) }
|
||||
|
||||
/** 构建上层(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, showNameOnly = true)
|
||||
return Seasoning22GridAdapter(
|
||||
largeSize = size.largeSize,
|
||||
smallSize = size.smallSize,
|
||||
showNameOnly = true
|
||||
)
|
||||
}
|
||||
|
||||
/** 配置两个 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 }
|
||||
layoutParams = layoutParams.also { it.height = size.rvHeight }
|
||||
layoutManager = FlexboxLayoutManager(requireContext()).apply {
|
||||
flexDirection = FlexDirection.COLUMN
|
||||
flexWrap = FlexWrap.WRAP
|
||||
@@ -205,7 +221,8 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
}
|
||||
// 加载油盆格子名称(address=1)
|
||||
val oilSlot = slotsOil.firstOrNull { it.address == 1 }
|
||||
binding.tvOilSlot.text = if (oilSlot != null && oilSlot.goodsName.isNotEmpty()) oilSlot.goodsName else "-"
|
||||
binding.tvOilSlot.text =
|
||||
if (oilSlot != null && oilSlot.goodsName.isNotEmpty()) oilSlot.goodsName else "-"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,7 +240,10 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
* - STATE_STABLE:无条件更新基准(覆盖取走调料/归零等场景)
|
||||
*/
|
||||
private fun addWeightListener() {
|
||||
Log.d(TAG, "addWeightListener: 注册监听, deviceId=${GlobalData.deviceId}, role=${GlobalData.deviceRole}")
|
||||
Log.d(
|
||||
TAG,
|
||||
"addWeightListener: 注册监听, deviceId=${GlobalData.deviceId}, role=${GlobalData.deviceRole}"
|
||||
)
|
||||
|
||||
if (GlobalData.deviceRole == DeviceRole.MASTER) {
|
||||
// 主设备:通过 allScales StateFlow 观测所有秤(含远端子设备秤)
|
||||
@@ -243,14 +263,21 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
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")
|
||||
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")
|
||||
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
|
||||
}
|
||||
@@ -263,7 +290,10 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
WeightUtil.addWeightListener(TAG) { address, state, weight ->
|
||||
val snapshotKey = "${GlobalData.deviceId}#$address"
|
||||
val baseline = weightSnapshot[snapshotKey]
|
||||
Log.d(TAG, "onWeight: address=$address state=$state weight=$weight baseline=$baseline")
|
||||
Log.d(
|
||||
TAG,
|
||||
"onWeight: address=$address state=$state weight=$weight baseline=$baseline"
|
||||
)
|
||||
|
||||
if (baseline == null) {
|
||||
weightSnapshot[snapshotKey] = weight
|
||||
@@ -271,7 +301,10 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
}
|
||||
|
||||
val delta = weight - baseline
|
||||
Log.d(TAG, "onWeight: address=$address state=$state weight=$weight baseline=$baseline delta=$delta")
|
||||
Log.d(
|
||||
TAG,
|
||||
"onWeight: address=$address state=$state weight=$weight baseline=$baseline delta=$delta"
|
||||
)
|
||||
|
||||
when {
|
||||
// 仅在秤稳定后判断增量,避免取走物品时手部冲力造成误触发
|
||||
@@ -280,6 +313,7 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
Log.d(TAG, "onWeight: 触发高亮 address=$address delta=$delta")
|
||||
triggerHighlight(GlobalData.deviceId, address, delta)
|
||||
}
|
||||
|
||||
state == WeightUtil.STATE_STABLE -> {
|
||||
weightSnapshot[snapshotKey] = weight
|
||||
}
|
||||
@@ -302,7 +336,10 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
val pos18 = if (sourceDeviceId == ScaleDeviceConfig.DEVICE_ID_18)
|
||||
ScaleDeviceConfig.SCALE_ORDER_18.indexOf(address) else -1
|
||||
|
||||
Log.d(TAG, "triggerHighlight: sourceDeviceId=$sourceDeviceId address=$address pos22=$pos22 pos18=$pos18")
|
||||
Log.d(
|
||||
TAG,
|
||||
"triggerHighlight: sourceDeviceId=$sourceDeviceId address=$address pos22=$pos22 pos18=$pos18"
|
||||
)
|
||||
|
||||
// 两个列表都不包含该地址,忽略
|
||||
if (pos22 < 0 && pos18 < 0) {
|
||||
@@ -318,6 +355,7 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
adapter22.highlightPosition(pos22)
|
||||
adapter18.clearHighlight()
|
||||
}
|
||||
|
||||
else -> {
|
||||
adapter18.highlightPosition(pos18)
|
||||
adapter22.clearHighlight()
|
||||
|
||||
@@ -64,7 +64,7 @@ class CameraUtils(private var activity: ComponentActivity) {
|
||||
)
|
||||
cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
|
||||
}
|
||||
bind()
|
||||
// bind() 由外部在 previewView attach 到窗口后调用,避免首次无预览
|
||||
}
|
||||
|
||||
// if (isCameraReady.not()) {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.shuwei.dish.match.utils
|
||||
|
||||
import com.shuwei.dish.match.base.BaseApp
|
||||
import com.shuwei.dish.match.utils.ext.dp
|
||||
|
||||
data class FlexLayoutSize(
|
||||
val smallSize: Int,
|
||||
val largeSize: Int,
|
||||
val rvHeight: Int
|
||||
)
|
||||
|
||||
object SizeTool {
|
||||
|
||||
fun getFlexLayoutSize(leftRightSpace: Int = 10.dp): FlexLayoutSize {
|
||||
// 上层:FlexboxLayoutManager,仿 MasterScaleActivity 中 Scale22VH 的布局方式
|
||||
// rvTop 高度 = 小格子尺寸 * 3行 + 3 * 间距,与 Scale22VH 保持一致
|
||||
val screenWidth = BaseApp.instance!!.resources.displayMetrics.widthPixels
|
||||
val usable = screenWidth - 8 * 4.dp - leftRightSpace
|
||||
val smallSize = usable / 9
|
||||
val largeSize = (usable - smallSize * 6) / 2
|
||||
val rvHeight = smallSize * 3 + 3 * 4.dp
|
||||
return FlexLayoutSize(smallSize, largeSize, rvHeight)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -173,6 +173,17 @@ class AppViewModel(private val rep: AppRepository) : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定设备是否已配置调料槽位
|
||||
* @param deviceId 目标设备 ID
|
||||
* @param action 回调,true 表示已配置,false 表示未配置
|
||||
*/
|
||||
fun hasSeasoningSlotConfig(deviceId: String, action: (Boolean) -> Unit) {
|
||||
viewModelScope.launch {
|
||||
action(rep.hasSeasoningSlotConfig(deviceId))
|
||||
}
|
||||
}
|
||||
|
||||
// fun updateAll(list: MutableList<SeasoningEntity>) {
|
||||
// viewModelScope.launch {
|
||||
// list.forEach {
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.shuwei.dish.match.viewmodel.factory
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.shuwei.dish.match.db.AppRepository
|
||||
import com.shuwei.dish.match.db.dao.SeasoningSlotDao
|
||||
import com.shuwei.dish.match.viewmodel.AppViewModel
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
@@ -59,6 +59,7 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/tvConnectionStatus"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:overScrollMode="never"
|
||||
tools:itemCount="4"
|
||||
tools:listitem="@layout/list_type_scale22"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -1,77 +1,172 @@
|
||||
<?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"
|
||||
tools:background="@color/bg_color"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- 底部导航菜单 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/bottomMenu"
|
||||
android:id="@+id/llCookMode"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="120dp"
|
||||
android:orientation="horizontal"
|
||||
android:background="@android:color/transparent"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:elevation="8dp">
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:background="@drawable/shape_white_e6_12_corners"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:clipToOutline="true">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnDeviceConfig"
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="设备切换"
|
||||
android:textSize="28sp"
|
||||
android:text="餐品模式"
|
||||
android:textSize="32sp"
|
||||
android:textStyle="bold"
|
||||
android:background="@android:color/transparent" />
|
||||
android:layout_marginStart="32dp"
|
||||
android:textColor="@color/black333" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnSeasoningConfig"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_weight="1"
|
||||
android:text="调料配置"
|
||||
<TextView
|
||||
android:id="@+id/tvCurrentMode"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="当前:制作模式"
|
||||
android:textSize="26sp"
|
||||
android:background="@android:color/transparent" />
|
||||
android:layout_marginStart="32dp"
|
||||
android:textColor="@color/black999" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnFoodCollect"
|
||||
<ImageView
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:src="@drawable/ic_arrow_right2"
|
||||
tools:ignore="ContentDescription" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llSeasoningConfig"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:background="@drawable/shape_white_e6_12_corners"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:clipToOutline="true">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="采集食材"
|
||||
android:textSize="26sp"
|
||||
android:background="@android:color/transparent" />
|
||||
android:text="调料区设置"
|
||||
android:textSize="32sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginStart="32dp"
|
||||
android:textColor="@color/black333" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:src="@drawable/ic_arrow_right2"
|
||||
tools:ignore="ContentDescription" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llFoodCollect"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:background="@drawable/shape_white_e6_12_corners"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:clipToOutline="true">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="食材采样"
|
||||
android:textSize="32sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginStart="32dp"
|
||||
android:textColor="@color/black333" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:src="@drawable/ic_arrow_right2"
|
||||
tools:ignore="ContentDescription" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- <!– 底部导航菜单 –>-->
|
||||
<!-- <LinearLayout-->
|
||||
<!-- android:id="@+id/bottomMenu"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:orientation="horizontal"-->
|
||||
<!-- android:background="@android:color/transparent"-->
|
||||
<!-- android:layout_marginTop="10dp"-->
|
||||
<!-- android:layout_marginBottom="10dp"-->
|
||||
<!-- android:layout_marginStart="15dp"-->
|
||||
<!-- android:layout_marginEnd="15dp"-->
|
||||
<!-- android:elevation="8dp">-->
|
||||
|
||||
<!-- <androidx.appcompat.widget.AppCompatButton-->
|
||||
<!-- android:id="@+id/btnDeviceRole"-->
|
||||
<!-- android:id="@+id/btnDeviceConfig"-->
|
||||
<!-- android:layout_width="0dp"-->
|
||||
<!-- android:layout_height="70dp"-->
|
||||
<!-- android:layout_weight="1"-->
|
||||
<!-- android:text="设备角色:子设备"-->
|
||||
<!-- android:textSize="22sp"-->
|
||||
<!-- android:text="设备切换"-->
|
||||
<!-- android:textSize="28sp"-->
|
||||
<!-- android:textStyle="bold"-->
|
||||
<!-- android:background="@android:color/transparent" />-->
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnScaleMonitor"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_weight="1"
|
||||
android:text="秤数据监控"
|
||||
android:textSize="26sp"
|
||||
android:visibility="visible"
|
||||
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" />-->
|
||||
|
||||
</LinearLayout>
|
||||
<!-- <androidx.appcompat.widget.AppCompatButton-->
|
||||
<!-- android:id="@+id/btnFoodCollect"-->
|
||||
<!-- android:layout_width="0dp"-->
|
||||
<!-- android:layout_height="70dp"-->
|
||||
<!-- android:layout_weight="1"-->
|
||||
<!-- android:text="采集食材"-->
|
||||
<!-- android:textSize="26sp"-->
|
||||
<!-- android:background="@android:color/transparent" />-->
|
||||
|
||||
<!-- Fragment 容器 -->
|
||||
<FrameLayout
|
||||
android:id="@+id/fragmentContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
<!-- <androidx.appcompat.widget.AppCompatButton-->
|
||||
<!-- android:id="@+id/btnScaleMonitor"-->
|
||||
<!-- android:layout_width="0dp"-->
|
||||
<!-- android:layout_height="70dp"-->
|
||||
<!-- android:layout_weight="1"-->
|
||||
<!-- android:text="秤数据监控"-->
|
||||
<!-- android:textSize="26sp"-->
|
||||
<!-- android:visibility="visible"-->
|
||||
<!-- android:background="@android:color/transparent" />-->
|
||||
|
||||
<!-- </LinearLayout>-->
|
||||
|
||||
<!-- <!– Fragment 容器 –>-->
|
||||
<!-- <FrameLayout-->
|
||||
<!-- android:id="@+id/fragmentContainer"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="0dp"-->
|
||||
<!-- android:layout_weight="1" />-->
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="660dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:overScrollMode="never" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -98,7 +100,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:includeFontPadding="false"
|
||||
android:text="千克"
|
||||
tools:text="千克"
|
||||
android:textColor="@color/black999"
|
||||
android:textSize="32sp" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -36,7 +36,9 @@
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/gray_eb" />
|
||||
|
||||
<!-- 2个按钮时显示(水平排列) -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutButtonsHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:gravity="center_vertical"
|
||||
@@ -69,4 +71,51 @@
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 3个按钮时显示(垂直排列,iOS风格) -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutButtonsVertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnTop"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/ripple_effect_light2"
|
||||
android:textColor="@color/dish_green"
|
||||
android:textSize="30sp" />
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/gray_eb" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnMiddle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/ripple_effect_light2"
|
||||
android:textColor="@color/dish_green"
|
||||
android:textSize="30sp" />
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/gray_eb" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnBottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/ripple_effect_light2"
|
||||
android:textColor="@color/dish_green"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginEnd="0dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/shape_white_30_corners"
|
||||
android:orientation="vertical">
|
||||
@@ -94,6 +94,8 @@
|
||||
android:background="@drawable/shape_scale_cell"
|
||||
android:gravity="center"
|
||||
android:text="-"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:clipToOutline="true"
|
||||
android:textColor="@color/home_title"
|
||||
android:textSize="28sp" />
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
android:layout_height="60dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
android:background="@drawable/shape_scale_cell"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:layout_marginHorizontal="4dp"
|
||||
android:layout_marginVertical="4dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSeasoningName"
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="4dp"
|
||||
android:background="@drawable/shape_scale_cell">
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:background="@drawable/shape_scale_cell"
|
||||
android:clipToOutline="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAddress"
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="340dp"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:background="@drawable/shape_scale_cell"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="8dp">
|
||||
android:paddingBottom="8dp"
|
||||
android:clipToOutline="true">
|
||||
|
||||
<!-- 秤编号 -->
|
||||
<TextView
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:clipToOutline="true"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:background="@drawable/shape_scale_cell">
|
||||
|
||||
<TextView
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="false"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="5dp">
|
||||
|
||||
|
||||
Reference in New Issue
Block a user