From 2de5ec39b54619d39e2d7ac8c51e75b43f8efd22 Mon Sep 17 00:00:00 2001 From: lvmeng <848755140@qq.com> Date: Wed, 6 May 2026 16:41:35 +0800 Subject: [PATCH] =?UTF-8?q?refactor(activity):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E9=A3=9F=E6=9D=90=E8=AF=86=E5=88=AB=E6=B5=81=E7=A8=8B=EF=BC=8C?= =?UTF-8?q?=E6=94=B9=E7=94=B1=E6=8E=A5=E5=8F=A3=E6=9F=A5=E8=AF=A2=E8=AF=86?= =?UTF-8?q?=E5=88=AB=E7=BB=93=E6=9E=9C=EF=BC=8CqueryGoodsList=20=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E5=85=B7=E5=90=8D=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dish/match/dialog/FoodSearchDialog.kt | 50 ++++---- .../match/dialog/SeasoningSelectDialog.kt | 19 ++- .../com/shuwei/dish/match/net/NetViewModel.kt | 24 +++- .../dish/match/ui/FoodRecognizeActivity.kt | 114 +++++++++++++----- .../dish/match/ui/PrepareFoodActivity.kt | 51 ++------ .../res/layout/activity_food_recognize.xml | 14 ++- .../main/res/layout/activity_prepare_food.xml | 2 +- 7 files changed, 165 insertions(+), 109 deletions(-) diff --git a/app/src/main/java/com/shuwei/dish/match/dialog/FoodSearchDialog.kt b/app/src/main/java/com/shuwei/dish/match/dialog/FoodSearchDialog.kt index 208eea1..baf63ce 100644 --- a/app/src/main/java/com/shuwei/dish/match/dialog/FoodSearchDialog.kt +++ b/app/src/main/java/com/shuwei/dish/match/dialog/FoodSearchDialog.kt @@ -15,8 +15,8 @@ import com.google.android.material.bottomsheet.BottomSheetDialog import com.shuwei.dish.match.R import com.shuwei.dish.match.adapter.FoodAdapter import com.shuwei.dish.match.base.BaseActivity -import com.shuwei.dish.match.base.BaseApp import com.shuwei.dish.match.databinding.DialogFoodSearchBinding +import com.shuwei.dish.match.dialog.SeasoningSelectDialog.Companion.PAGE_SIZE import com.shuwei.dish.match.entity.GoodsItem import com.shuwei.dish.match.net.UiState import com.shuwei.dish.match.utils.KeyboardUtil @@ -36,20 +36,25 @@ class FoodSearchDialog( private val onItemSelected: (item: GoodsItem) -> Unit ) : BottomSheetDialog(activity, R.style.BottomSheet) { - private val binding = DialogFoodSearchBinding.inflate(LayoutInflater.from(activity)) - private val list = mutableListOf().apply { - add(GoodsItem(goodsId = "200001", goodsName = "土豆丝")) - add(GoodsItem(goodsId = "200002", goodsName = "土豆片")) - add(GoodsItem(goodsId = "200003", goodsName = "土豆丁")) - add(GoodsItem(goodsId = "200004", goodsName = "胡萝卜丝")) - add(GoodsItem(goodsId = "200005", goodsName = "胡萝卜片")) - add(GoodsItem(goodsId = "200006", goodsName = "胡萝卜丁")) - add(GoodsItem(goodsId = "200007", goodsName = "黄瓜丝")) - add(GoodsItem(goodsId = "200008", goodsName = "黄瓜片")) - add(GoodsItem(goodsId = "200009", goodsName = "黄瓜丁")) - add(GoodsItem(goodsId = "200010", goodsName = "洋葱丝")) - add(GoodsItem(goodsId = "200011", goodsName = "洋葱丁")) + companion object { + const val PAGE_SIZE = 50 } + + private val binding = DialogFoodSearchBinding.inflate(LayoutInflater.from(activity)) + private val list = mutableListOf() +// .apply { +// add(GoodsItem(goodsId = "200001", goodsName = "土豆丝")) +// add(GoodsItem(goodsId = "200002", goodsName = "土豆片")) +// add(GoodsItem(goodsId = "200003", goodsName = "土豆丁")) +// add(GoodsItem(goodsId = "200004", goodsName = "胡萝卜丝")) +// add(GoodsItem(goodsId = "200005", goodsName = "胡萝卜片")) +// add(GoodsItem(goodsId = "200006", goodsName = "胡萝卜丁")) +// add(GoodsItem(goodsId = "200007", goodsName = "黄瓜丝")) +// add(GoodsItem(goodsId = "200008", goodsName = "黄瓜片")) +// add(GoodsItem(goodsId = "200009", goodsName = "黄瓜丁")) +// add(GoodsItem(goodsId = "200010", goodsName = "洋葱丝")) +// add(GoodsItem(goodsId = "200011", goodsName = "洋葱丁")) +// } private val adapter = FoodAdapter(list).apply { setOnItemClickListener { _, _, position -> list[position].isClicked = true @@ -63,7 +68,6 @@ class FoodSearchDialog( private var goodsName: String? = null private var pageNo = 1 - private val pageSize = 50 init { setContentView(binding.root) @@ -160,16 +164,12 @@ class FoodSearchDialog( * 请求食材列表 */ private fun getGoodsList() { - val param = mutableMapOf( - "goodsType" to "0", - "placeId" to BaseApp.canteenId, - "pageNum" to pageNo, - "pageSize" to pageSize + activity.netViewModel.queryGoodsList( + goodsType = "0", + pageNum = pageNo, + pageSize = PAGE_SIZE, + goodsName = goodsName.takeUnless { it.isNullOrBlank() } ) - if (!goodsName.isNullOrBlank()) { - param["goodsName"] = goodsName!! - } - activity.netViewModel.queryGoodsList(param) } /** @@ -185,7 +185,7 @@ class FoodSearchDialog( if (pageNo == 1) list.clear() list.addAll(records) adapter.notifyDataSetChanged() - val isLoadMoreEnable = records.size >= pageSize + val isLoadMoreEnable = records.size >= PAGE_SIZE binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable) if (isLoadMoreEnable) pageNo++ } diff --git a/app/src/main/java/com/shuwei/dish/match/dialog/SeasoningSelectDialog.kt b/app/src/main/java/com/shuwei/dish/match/dialog/SeasoningSelectDialog.kt index 6d495b4..214aa34 100644 --- a/app/src/main/java/com/shuwei/dish/match/dialog/SeasoningSelectDialog.kt +++ b/app/src/main/java/com/shuwei/dish/match/dialog/SeasoningSelectDialog.kt @@ -20,7 +20,6 @@ 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.base.BaseApp import com.shuwei.dish.match.databinding.DialogSeasoningSelectBinding import com.shuwei.dish.match.entity.GoodsItem import com.shuwei.dish.match.entity.SeasoningEntity @@ -43,6 +42,7 @@ class SeasoningSelectDialog( companion object { const val TAG = "SeasoningSelectDialog" + const val PAGE_SIZE = 50 } private val binding = DialogSeasoningSelectBinding.inflate(LayoutInflater.from(activity)) @@ -60,7 +60,6 @@ class SeasoningSelectDialog( private var goodsName: String? = null private var pageNo = 1 - private val pageSize = 50 private var collectJob: Job? = null init { @@ -181,16 +180,12 @@ class SeasoningSelectDialog( * 请求调料列表 */ private fun getGoodsList() { - val param = mutableMapOf( - "goodsType" to "1", - "placeId" to BaseApp.canteenId, - "pageNum" to pageNo, - "pageSize" to pageSize + activity.netViewModel.queryGoodsList( + goodsType = "1", + pageNum = pageNo, + pageSize = PAGE_SIZE, + goodsName = goodsName.takeUnless { it.isNullOrBlank() } ) - if (!goodsName.isNullOrBlank()) { - param["goodsName"] = goodsName!! - } - activity.netViewModel.queryGoodsList(param) } /** @@ -206,7 +201,7 @@ class SeasoningSelectDialog( if (pageNo == 1) list.clear() list.addAll(records.map { it.toSeasoningEntity() }) adapter.notifyDataSetChanged() - val isLoadMoreEnable = records.size >= pageSize + val isLoadMoreEnable = records.size >= PAGE_SIZE binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable) if (isLoadMoreEnable) pageNo++ } diff --git a/app/src/main/java/com/shuwei/dish/match/net/NetViewModel.kt b/app/src/main/java/com/shuwei/dish/match/net/NetViewModel.kt index 927f712..ca960ac 100644 --- a/app/src/main/java/com/shuwei/dish/match/net/NetViewModel.kt +++ b/app/src/main/java/com/shuwei/dish/match/net/NetViewModel.kt @@ -2,6 +2,7 @@ package com.shuwei.dish.match.net import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.shuwei.dish.match.base.BaseApp import com.shuwei.dish.match.entity.CookFoodEntity import com.shuwei.dish.match.entity.FoodRecord import com.shuwei.dish.match.entity.GoodsItem @@ -93,8 +94,29 @@ class NetViewModel( /** * 查询物品信息列表(食材 goodsType=0,调料 goodsType=1 共用) + * @param goodsType 物品类型:0=食材,1=调料 + * @param placeId 场所 ID,默认取当前食堂 ID + * @param pageNum 页码,默认第 1 页 + * @param pageSize 每页条数,默认 50 + * @param goodsName 按名称模糊搜索,为 null 时不传该字段 + * @param goodsNames 按名称列表批量查询,为 null 时不传该字段 */ - fun queryGoodsList(param: MutableMap) { + fun queryGoodsList( + goodsType: String, + placeId: String = BaseApp.canteenId, + pageNum: Int = 1, + pageSize: Int = 50, + goodsName: String? = null, + goodsNames: List? = null + ) { + val param = mutableMapOf( + "goodsType" to goodsType, + "placeId" to placeId, + "pageNum" to pageNum, + "pageSize" to pageSize + ) + goodsName?.let { param["goodsName"] = it } + goodsNames?.let { param["goodsNames"] = it } viewModelScope.launch { _goodsListState.value = UiState.Loading _goodsListState.value = repository.queryGoodsList(param) diff --git a/app/src/main/java/com/shuwei/dish/match/ui/FoodRecognizeActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/FoodRecognizeActivity.kt index f452de8..47e869f 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/FoodRecognizeActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/FoodRecognizeActivity.kt @@ -19,7 +19,12 @@ import com.shuwei.dish.match.utils.ext.gone import com.shuwei.dish.match.utils.ext.visible import androidx.core.net.toUri import androidx.core.view.updateLayoutParams +import androidx.lifecycle.lifecycleScope +import com.shuwei.dish.match.net.UiState +import com.shuwei.dish.match.objbox.FoodModule import com.shuwei.dish.match.utils.ext.dp +import com.shuwei.dish.match.utils.ext.toast +import kotlinx.coroutines.launch /** * 食材识别结果页面 @@ -34,24 +39,23 @@ class FoodRecognizeActivity : BaseActivity() { const val EXTRA_SELECTED_ITEM = "extra_selected_item" const val IS_MANUAL_CANCEL = "isManualCancel" - /** 内存缓存食材列表,避免通过 Intent 序列化传递大数据 */ - private var pendingGoodsList: ArrayList? = null + private var recognizeNameList: ArrayList? = null /** * 启动本页面,通过 onResult 回调返回用户选中的食材 * @param activity 宿主 Activity * @param imageUri 食材照片路径(可为空) - * @param goodsList 识别到的食材列表 + * @param nameScoreList 识别到的食材列表 * @param finishCallback 页面关闭后回调,携带用户选中的食材(未选中时为 null) */ fun start( activity: BaseActivity, imageUri: String? = null, currentWeight: Double = 0.0, - goodsList: ArrayList = arrayListOf(), + nameScoreList: ArrayList = arrayListOf(), finishCallback: (Boolean, GoodsItem?) -> Unit = { _, _ -> } ) { - pendingGoodsList = goodsList + recognizeNameList = nameScoreList val launchIntent = Intent(activity, FoodRecognizeActivity::class.java).apply { putExtra(EXTRA_IMAGE_URI, imageUri) putExtra(EXTRA_CURRENT_WEIGHT, currentWeight) @@ -62,7 +66,8 @@ class FoodRecognizeActivity : BaseActivity() { IntentCompat.getSerializableExtra( it, EXTRA_SELECTED_ITEM, - GoodsItem::class.java) + GoodsItem::class.java + ) } finishCallback(isManualCancel, entity) } @@ -102,6 +107,12 @@ class FoodRecognizeActivity : BaseActivity() { /** 食材搜索弹窗,持有引用防止重复打开 */ private var foodSearchDialog: FoodSearchDialog? = null + /** 通过name查询IdNameScore */ + private val nameScoreMap = mutableMapOf() + + /** 识别物品名称列表 */ + private val goodsNameList = mutableListOf() + private val scrollListener = object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { updateScrollIndicator() @@ -136,8 +147,6 @@ class FoodRecognizeActivity : BaseActivity() { } } ) - - initData() initRecyclerView() updateRvParentLayoutHeight() initClickListeners() @@ -145,6 +154,8 @@ class FoodRecognizeActivity : BaseActivity() { onBackPressedDispatcher.addCallback(this) { manualBack() } + initObserver() + initData() } /** @@ -166,23 +177,75 @@ class FoodRecognizeActivity : BaseActivity() { currentWeight = intent.getDoubleExtra(EXTRA_CURRENT_WEIGHT, 0.0) binding.tvWeight.text = "${currentWeight}g" - // 取出内存缓存的食材列表,取用后立即清空 - val goodsList = pendingGoodsList?.also { pendingGoodsList = null } - if (!goodsList.isNullOrEmpty()) { - list.addAll(goodsList) - // 默认选中第一项 - selectedPosition = 0 - list[0].isClicked = true - list[0].useWeight = currentWeight - binding.tvSelectedFood.text = list[0].goodsName ?: "-" + recognizeNameList?.apply { + forEach { + val goodsName = it.name.split("WP").first() + goodsNameList.add(goodsName) + nameScoreMap.put(goodsName, it) + } + recognizeNameList = null } + netViewModel.queryGoodsList( + goodsType = "0", + goodsNames = goodsNameList + ) + } + + private fun initObserver() { + lifecycleScope.launch { + netViewModel.goodsListState.collect { state -> + when (state) { + is UiState.Loading -> { + showLoading() + } + + is UiState.Success -> { + delayDismissLoading() + loadRecognizeList(state.data) + } + + is UiState.Error -> { + delayDismissLoading() + toast(state.msg) + } + + is UiState.Idle -> {} + } + } + } + } + + private fun loadRecognizeList(goodsList: List?) { + if (goodsList.isNullOrEmpty()) { + binding.flListContainer.updateLayoutParams { + height = 260.dp + } + binding.rvRecognizeList.gone() + binding.tvNoData.visible() + return + } + binding.rvRecognizeList.visible() + binding.tvNoData.gone() + list.clear() + goodsList.forEach { + val food = nameScoreMap[it.goodsName] + val foodScore = ((1 - (food?.score ?: 0.0)) * 10000).toInt() + list.add(it.also { it.foodScore = foodScore }) + } + adapter.notifyDataSetChanged() + updateRvParentLayoutHeight() + // 默认选中第一项 + selectedPosition = 0 + list[0].isClicked = true + list[0].useWeight = currentWeight + binding.tvSelectedFood.text = list[0].goodsName ?: "-" } /** * 初始化 RecyclerView */ private fun initRecyclerView() { - binding.recyclerView.let { + binding.rvRecognizeList.let { it.layoutManager = GridLayoutManager(this@FoodRecognizeActivity, 2) it.adapter = adapter it.isVerticalScrollBarEnabled = false @@ -206,11 +269,8 @@ class FoodRecognizeActivity : BaseActivity() { * 根据列表是否有数据控制 RecyclerView 显隐 */ private fun updateListVisibility() { - if (list.isEmpty()) { - binding.flListContainer.gone() - } else { - binding.flListContainer.visible() - binding.recyclerView.post { updateScrollIndicator() } + if (list.isNotEmpty()) { + binding.rvRecognizeList.post { updateScrollIndicator() } } } @@ -218,7 +278,7 @@ class FoodRecognizeActivity : BaseActivity() { * 根据 RecyclerView 滚动状态刷新右侧自定义指示条 */ private fun updateScrollIndicator() { - val recyclerView = binding.recyclerView + val recyclerView = binding.rvRecognizeList val track = binding.vScrollIndicatorTrack val thumb = binding.vScrollIndicatorThumb @@ -295,7 +355,7 @@ class FoodRecognizeActivity : BaseActivity() { updateRvParentLayoutHeight() binding.tvSelectedFood.text = item.goodsName ?: "-" updateListVisibility() - binding.recyclerView.post { updateScrollIndicator() } + binding.rvRecognizeList.post { updateScrollIndicator() } } /** @@ -336,8 +396,8 @@ class FoodRecognizeActivity : BaseActivity() { * 取消待执行的延迟重量更新任务 */ private fun cancelPendingWeightUpdate() { - // pendingWeightRunnable?.let { handler.removeCallbacks(it) } - // pendingWeightRunnable = null + // pendingWeightRunnable?.let { handler.removeCallbacks(it) } + // pendingWeightRunnable = null } /** diff --git a/app/src/main/java/com/shuwei/dish/match/ui/PrepareFoodActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/PrepareFoodActivity.kt index 0657818..d8062f0 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/PrepareFoodActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/PrepareFoodActivity.kt @@ -240,7 +240,7 @@ class PrepareFoodActivity : BaseActivity() { it.isSetFinished = true }) - binding.rvDishPartList.smoothScrollToPosition(list.size - 1) + binding.rvMaterialList.smoothScrollToPosition(list.size - 1) } } @@ -357,7 +357,7 @@ class PrepareFoodActivity : BaseActivity() { * RecyclerView初始化 */ private fun initRecyclerView() { - binding.rvDishPartList.run { + binding.rvMaterialList.run { layoutManager = LinearLayoutManager(this@PrepareFoodActivity, LinearLayoutManager.VERTICAL, false) // 仅 isNewDishType=true 的 item 显示侧滑删除菜单(必须在 setAdapter 之前调用) @@ -489,7 +489,11 @@ class PrepareFoodActivity : BaseActivity() { return@withContext } withContext(Dispatchers.Main) { - queryFood(foodScoreList) + Log.d(TAG, "takePhoto queryFood, list=$foodScoreList") + postDelayed(1000) { + dismissLoading() + loadRecognizeResultPage(foodScoreList) + } } } } @@ -505,47 +509,10 @@ class PrepareFoodActivity : BaseActivity() { dismissLoading() } - /** - * 调用接口查询识别到的菜品 - * @param list 食材列表 - */ - private fun queryFood(list: List) { - Log.d(TAG, "takePhoto queryFood, list=$list") - //调用接口成功,返回食材列表设置isTakingPhoto.set(false),暂时写死数据 - val foodList = mutableListOf() - list.forEachIndexed { index, food -> - Log.d(TAG, "takePhoto queryFood, index=${index},food=$food") - var foodName = "" - var foodId = "" - food.name.split("WP").let { - foodName = it[0] - foodId = if (it.size > 1) { - "WP${it[1]}" - } else { - food.name - } - } - foodList.add( - GoodsItem( - goodsId = foodId, - goodsName = foodName - ).also { - it.foodScore = ((1 - food.score) * 10000).toInt() - it.isOriginalData = false - } - ) - } - Log.d(TAG, "takePhoto queryFood, 数据添加完成") - postDelayed(1000) { - dismissLoading() - loadRecognizeResultPage(foodList) - } - } - /** * 加载识别结果页面 */ - private fun loadRecognizeResultPage(foodList: List) { + private fun loadRecognizeResultPage(nameScoreList: List) { if (currentWeight < WEIGHT_RECOGNIZE_VALUE) { Log.d(TAG, "takePhoto queryFood, 已取消") toast("已取消") @@ -557,7 +524,7 @@ class PrepareFoodActivity : BaseActivity() { activity = this, imageUri = lastPhotoUri?.toString(), currentWeight = currentWeight, - goodsList = ArrayList(foodList), + nameScoreList = ArrayList(nameScoreList), finishCallback = { isManualCancel, entity -> Log.d(TAG, "queryFood: isManualCancel=$isManualCancel, entity=${entity.toJsonString()}") manualCancelFlag = isManualCancel diff --git a/app/src/main/res/layout/activity_food_recognize.xml b/app/src/main/res/layout/activity_food_recognize.xml index 7f0ef14..dded883 100644 --- a/app/src/main/res/layout/activity_food_recognize.xml +++ b/app/src/main/res/layout/activity_food_recognize.xml @@ -35,7 +35,7 @@ android:layout_marginTop="12dp"> + +