From ec4c3f1232d5a0f8cde75fbd825acac6a2b5f1a0 Mon Sep 17 00:00:00 2001 From: lvmeng <848755140@qq.com> Date: Mon, 11 May 2026 12:07:06 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=8F=90=E5=8F=96=E5=87=80?= =?UTF-8?q?=E6=9D=90=E7=A7=8D=E7=B1=BB=E5=BC=B9=E7=AA=97=E4=B8=BA=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E7=B1=BB=E5=B9=B6=E7=BB=9F=E4=B8=80=E5=90=84=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E9=80=89=E6=8B=A9=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 RawMaterialsDialogUtils,包含 showRawMaterialsDialog、 buildSingleChoiceChipGroup、buildMockGoodsList 三个顶层函数 - FoodAdapter/list_item_food 新增净材种类选中结果展示 - VectorCollectionFragment、FoodRecognizeActivity、FoodSearchDialog 的食材点击均改为弹出净材种类单选弹窗后再确认选中 - FoodSearchDialog 空布局补充点击加载测试数据 - setting_border 新增圆角 --- .../shuwei/dish/match/adapter/FoodAdapter.kt | 8 + .../dish/match/dialog/FoodSearchDialog.kt | 76 ++++---- .../dish/match/ui/FoodRecognizeActivity.kt | 25 ++- .../ui/fragment/VectorCollectionFragment.kt | 172 +++--------------- .../match/utils/RawMaterialsDialogUtils.kt | 151 +++++++++++++++ app/src/main/res/drawable/setting_border.xml | 1 + app/src/main/res/layout/list_item_food.xml | 56 ++++-- 7 files changed, 278 insertions(+), 211 deletions(-) create mode 100644 app/src/main/java/com/shuwei/dish/match/utils/RawMaterialsDialogUtils.kt diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/FoodAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/FoodAdapter.kt index 6bf489f..f418f16 100644 --- a/app/src/main/java/com/shuwei/dish/match/adapter/FoodAdapter.kt +++ b/app/src/main/java/com/shuwei/dish/match/adapter/FoodAdapter.kt @@ -48,6 +48,14 @@ class FoodAdapter(list: MutableList) : it.text = foodScoreText } } + if (item.rawMaterialsType.isNullOrBlank()) { + holder.binding.tvRawMaterialsType.gone() + } else { + holder.binding.tvRawMaterialsType.let { + it.visible() + it.text = item.rawMaterialsType + } + } } override fun onCreateViewHolder( 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 a325699..f6808b0 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 @@ -18,6 +18,7 @@ import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.chad.library.adapter4.util.setOnDebouncedItemClick import com.google.android.material.bottomsheet.BottomSheetDialog import com.shuwei.dish.match.R import com.shuwei.dish.match.adapter.FoodAdapter @@ -28,6 +29,8 @@ 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 +import com.shuwei.dish.match.utils.buildMockGoodsList +import com.shuwei.dish.match.utils.showRawMaterialsDialog import com.shuwei.dish.match.utils.ext.addOnActionSearchListener import com.shuwei.dish.match.utils.ext.toast import kotlinx.coroutines.launch @@ -50,36 +53,25 @@ class FoodSearchDialog( 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 = "洋葱丁")) -// add(GoodsItem(goodsId = "200012", goodsName = "A")) -// add(GoodsItem(goodsId = "200013", goodsName = "B")) -// add(GoodsItem(goodsId = "200014", goodsName = "C")) -// add(GoodsItem(goodsId = "200015", goodsName = "D")) -// add(GoodsItem(goodsId = "200016", goodsName = "E")) -// add(GoodsItem(goodsId = "200017", goodsName = "F")) -// add(GoodsItem(goodsId = "200018", goodsName = "G")) -// add(GoodsItem(goodsId = "200019", goodsName = "H")) -// } + private val adapter = FoodAdapter(list).apply { isStateViewEnable = true - setOnItemClickListener { _, _, position -> - list[position].isClicked = true - notifyItemChanged(position) - binding.root.postDelayed({ - onItemSelected(list[position]) - dismiss() - }, 300) + setOnDebouncedItemClick { _, _, position -> + val item = list[position] + // 弹出净材种类单选弹窗,确认后再回调并关闭弹窗 + showRawMaterialsDialog( + context = activity, + types = item.rawMaterialsTypes, + checkedType = item.rawMaterialsType + ) { selectedType -> + item.rawMaterialsType = selectedType + item.isClicked = true + notifyItemChanged(position) + binding.root.postDelayed({ + onItemSelected(item) + dismiss() + }, 300) + } } } @@ -241,6 +233,11 @@ class FoodSearchDialog( } binding.recyclerView.layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.VERTICAL, false) if (pageNo == 1) list.clear() + // 将接口返回的 rawMaterialsType(全部种类)移存到 rawMaterialsTypes,供弹窗使用 + records.forEach { + it.rawMaterialsTypes = it.rawMaterialsType + it.rawMaterialsType = null + } list.addAll(records) adapter.notifyDataSetChanged() val isLoadMoreEnable = records.size >= PAGE_SIZE @@ -269,16 +266,19 @@ class FoodSearchDialog( false ) } - emptyViewBinding!!.tvContent.text = "暂无数据" - emptyViewBinding!!.tvSubContent.text = "未查询到食材信息,请联系管理员添加" - emptyViewBinding!!.root.layoutParams = FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.MATCH_PARENT - ) - binding.recyclerView.layoutManager = LinearLayoutManager(activity) - binding.recyclerView.post { - emptyViewBinding!!.root.minimumHeight = binding.recyclerView.height - adapter.stateView = emptyViewBinding!!.root + emptyViewBinding?.let { + it.tvContent.text = "暂无数据" + it.tvSubContent.text = "未查询到食材信息,请联系管理员添加" + it.root.setOnClickListener { loadGoodsList(buildMockGoodsList().toMutableList()) } + it.root.layoutParams = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT + ) + binding.recyclerView.layoutManager = LinearLayoutManager(activity) + binding.recyclerView.post { + it.root.minimumHeight = binding.recyclerView.height + adapter.stateView = it.root + } } } catch (e: Exception) { e.printStackTrace() 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 d95f17f..276f53a 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 @@ -25,6 +25,7 @@ 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 com.shuwei.dish.match.utils.showRawMaterialsDialog import kotlinx.coroutines.launch /** @@ -122,12 +123,21 @@ class FoodRecognizeActivity : BaseActivity() { private val adapter = FoodAdapter(list).apply { setOnItemClickListener { _, _, position -> - list.forEachIndexed { i, item -> item.isClicked = (i == position) } - // 点击选择时记录当前秤重量 - list[position].useWeight = currentWeight - notifyDataSetChanged() - selectedPosition = position - binding.tvSelectedFood.text = list[position].goodsName ?: "-" + val item = list[position] + // 弹出净材种类单选弹窗,确认后再选中该食材 + showRawMaterialsDialog( + context = this@FoodRecognizeActivity, + types = item.rawMaterialsTypes, + checkedType = item.rawMaterialsType + ) { selectedType -> + item.rawMaterialsType = selectedType + list.forEachIndexed { i, it -> it.isClicked = (i == position) } + // 点击选择时记录当前秤重量 + item.useWeight = currentWeight + notifyDataSetChanged() + selectedPosition = position + binding.tvSelectedFood.text = item.goodsName ?: "-" + } } } @@ -260,6 +270,9 @@ class FoodRecognizeActivity : BaseActivity() { goodsList.forEach { val food = nameScoreMap[it.goodsName] val foodScore = ((1 - (food?.score ?: 0.0)) * 10000).toInt() + // 将接口返回的 rawMaterialsType(全部种类)移存到 rawMaterialsTypes,供弹窗使用 + it.rawMaterialsTypes = it.rawMaterialsType + it.rawMaterialsType = null list.add(it.also { it.foodScore = foodScore }) } adapter.notifyDataSetChanged() diff --git a/app/src/main/java/com/shuwei/dish/match/ui/fragment/VectorCollectionFragment.kt b/app/src/main/java/com/shuwei/dish/match/ui/fragment/VectorCollectionFragment.kt index 49b2734..4d79b4c 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/fragment/VectorCollectionFragment.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/fragment/VectorCollectionFragment.kt @@ -3,7 +3,6 @@ package com.shuwei.dish.match.ui.fragment import android.annotation.SuppressLint import android.content.Intent import android.content.Context -import android.content.res.ColorStateList import android.graphics.Bitmap import android.net.Uri import android.util.Log @@ -12,10 +11,7 @@ import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager -import com.google.android.material.chip.Chip -import com.google.android.material.chip.ChipGroup import androidx.camera.view.PreviewView -import androidx.core.content.ContextCompat import androidx.core.view.doOnLayout import androidx.core.view.updateLayoutParams import androidx.lifecycle.lifecycleScope @@ -32,7 +28,6 @@ import com.shuwei.dish.match.base.BaseFragment import com.shuwei.dish.match.databinding.FragmentVectorCollectionBinding import com.shuwei.dish.match.databinding.LayoutCameraPreviewBinding import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding -import com.shuwei.dish.match.dialog.CommonDialog import com.shuwei.dish.match.dialog.Loading import com.shuwei.dish.match.entity.FoodCollectionBean import com.shuwei.dish.match.entity.GoodsItem @@ -48,6 +43,9 @@ import com.shuwei.dish.match.utils.Debouncer import com.shuwei.dish.match.utils.ImageUploader import com.shuwei.dish.match.utils.ImageUtil import com.shuwei.dish.match.utils.LogSaveUtil +import com.shuwei.dish.match.utils.buildSingleChoiceChipGroup +import com.shuwei.dish.match.utils.buildMockGoodsList +import com.shuwei.dish.match.utils.showRawMaterialsDialog import com.shuwei.dish.match.utils.ext.clickWithDebounce import com.shuwei.dish.match.utils.ext.dp import com.shuwei.dish.match.utils.ext.gone @@ -81,9 +79,27 @@ class VectorCollectionFragment : BaseFragment() isStateViewEnable = true setOnDebouncedItemClick { _, _, position -> val item = searchList[position] - val types = item.rawMaterialsTypes // 弹出净材种类单选弹窗 - showRawMaterialsDialog(item, position, types) + showRawMaterialsDialog( + context = requireContext(), + types = item.rawMaterialsTypes, + checkedType = item.rawMaterialsType + ) { selectedType -> + // 清空其他已选项的状态 + searchList.forEachIndexed { index, it -> + if (index != position) { + it.isClicked = false + it.rawMaterialsType = null + } + } + // 设置当前选中项 + item.isClicked = true + item.rawMaterialsType = selectedType + checkedItem = item + selectedFoodId = item.goodsId + selectedFoodName = item.goodsName + notifyDataSetChanged() + } } } } @@ -424,148 +440,6 @@ class VectorCollectionFragment : BaseFragment() } } - /** - * 构造用于效果测试的食材假数据 - * TODO: 联调完成后删除 - */ - private fun buildMockGoodsList(): List = mutableListOf( - GoodsItem(goodsId = "1", goodsName = "土豆", rawMaterialsType = "土豆丝,土豆条,土豆片,土豆丁"), - GoodsItem(goodsId = "2", goodsName = "胡萝卜", rawMaterialsType = "胡萝卜丝,胡萝卜片,胡萝卜丁"), - GoodsItem(goodsId = "3", goodsName = "黄瓜", rawMaterialsType = "黄瓜丝,黄瓜片,黄瓜丁,黄瓜块"), - GoodsItem(goodsId = "4", goodsName = "洋葱", rawMaterialsType = "洋葱丝,洋葱丁,洋葱圈"), - GoodsItem(goodsId = "5", goodsName = "白菜", rawMaterialsType = "白菜丝,白菜块,白菜叶"), - GoodsItem(goodsId = "6", goodsName = "豆腐", rawMaterialsType = "豆腐块,豆腐丁"), - GoodsItem(goodsId = "7", goodsName = "猪肉", rawMaterialsType = "猪肉丝,猪肉片,猪肉丁,猪肉块"), - GoodsItem(goodsId = "8", goodsName = "鸡胸肉", rawMaterialsType = "鸡胸肉丝,鸡胸肉片,鸡胸肉丁"), - GoodsItem(goodsId = "9", goodsName = "牛肉", rawMaterialsType = "牛肉丝,牛肉片,牛肉块"), - GoodsItem(goodsId = "10", goodsName = "虾仁", rawMaterialsType = "整虾仁,切段虾仁"), - GoodsItem(goodsId = "11", goodsName = "茄子", rawMaterialsType = "茄子丝,茄子片,茄子块,茄子条"), - GoodsItem(goodsId = "12", goodsName = "青椒", rawMaterialsType = "青椒丝,青椒片,青椒块"), - GoodsItem(goodsId = "13", goodsName = "西红柿", rawMaterialsType = "西红柿片,西红柿块,西红柿丁"), - GoodsItem(goodsId = "14", goodsName = "芹菜", rawMaterialsType = "芹菜段,芹菜丝"), - GoodsItem(goodsId = "15", goodsName = "莲藕", rawMaterialsType = "莲藕片,莲藕丁,莲藕丝"), - // 以下5条 rawMaterialsType 为空,测试无净材数据场景 - GoodsItem(goodsId = "16", goodsName = "菠菜"), - GoodsItem(goodsId = "17", goodsName = "生菜"), - GoodsItem(goodsId = "18", goodsName = "香菇"), - GoodsItem(goodsId = "19", goodsName = "木耳"), - GoodsItem(goodsId = "20", goodsName = "豆芽"), - ) - - /** - * 弹出净材种类单选弹窗 - * @param item 当前点击的食材项 - * @param position 在 searchList 中的位置 - * @param types 净材种类字符串(逗号分隔),为空时提示无数据 - */ - @SuppressLint("NotifyDataSetChanged") - private fun showRawMaterialsDialog(item: GoodsItem, position: Int, types: String?) { - val typeList = types?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() } ?: emptyList() - - val dialog = CommonDialog(requireContext()) - .setTitle("选择净材种类") - .setNegativeButton("取消") - - if (typeList.isEmpty()) { - // 无净材数据时仅展示提示文字 - dialog.setContent("无净材数据") - .setPositiveButton("确认") {} - } else { - // 构建单选 ChipGroup 作为自定义内容 - val chipGroup = buildSingleChoiceChipGroup(typeList, item.rawMaterialsType) - dialog.addContentView(chipGroup) { tvContent -> - tvContent.gone() - } - dialog.setPositiveButtonInterceptable("确认") { - val checkedId = chipGroup.checkedChipId - if (checkedId == View.NO_ID) { - toast("请选择净材种类") - // 返回 false 阻止弹窗关闭 - return@setPositiveButtonInterceptable false - } - val selectedType = typeList[checkedId] - // 清空其他已选项的状态 - searchList.forEachIndexed { index, it -> - if (index != position) { - it.isClicked = false - it.rawMaterialsType = null - } - } - // 设置当前选中项 - item.isClicked = true - item.rawMaterialsType = selectedType - checkedItem = item - selectedFoodId = item.goodsId - selectedFoodName = item.goodsName - searchAdapter.notifyDataSetChanged() - true - } - } - - dialog.show() - } - - /** - * 构建单选样式的 ChipGroup - * @param typeList 选项文本列表,列表中每一项作为一个 Chip 的文字 - * @param checkedType 默认选中项的文本,与 typeList 中元素相等的 Chip 会被勾选;为空则无默认选中 - * @return 配置完成的 ChipGroup,每个 Chip 的 id 即为其在 typeList 中的下标 - */ - private fun buildSingleChoiceChipGroup( - typeList: List, - checkedType: String? - ): ChipGroup { - return ChipGroup(requireContext()).apply { - isSingleSelection = true - isSelectionRequired = false - setPadding(5.dp, 40.dp, 5.dp, 40.dp) - chipSpacingVertical = 10.dp - chipSpacingHorizontal = 20.dp - typeList.forEachIndexed { index, typeName -> - val chip = Chip(requireContext()).apply { - id = index - text = typeName - textSize = 22f - isCheckable = true - chipStartPadding = 20.dp.toFloat() - chipEndPadding = 20.dp.toFloat() - chipMinHeight = (textSize + 15.dp * 2).toFloat() - // 选中/未选中态背景色:主题绿 / 浅灰 - chipBackgroundColor = ColorStateList( - arrayOf( - intArrayOf(android.R.attr.state_checked), - intArrayOf(-android.R.attr.state_checked) - ), - intArrayOf( - ContextCompat.getColor(requireContext(), R.color.dish_green), - ContextCompat.getColor(requireContext(), R.color.gray_eb) - ) - ) - // 选中/未选中态文字色:白 / 深灰 - setTextColor( - ColorStateList( - arrayOf( - intArrayOf(android.R.attr.state_checked), - intArrayOf(-android.R.attr.state_checked) - ), - intArrayOf( - ContextCompat.getColor(requireContext(), R.color.white), - ContextCompat.getColor(requireContext(), R.color.black333) - ) - ) - ) - // 隐藏默认的左侧勾选图标,让填充色作为唯一选中反馈 - isCheckedIconVisible = false - // 去除描边 - chipStrokeWidth = 0f - // 若当前项已有选中值则默认勾选对应选项 - isChecked = (typeName == checkedType) - } - addView(chip) - } - } - } - var clickIndex = -1 @SuppressLint("NotifyDataSetChanged") diff --git a/app/src/main/java/com/shuwei/dish/match/utils/RawMaterialsDialogUtils.kt b/app/src/main/java/com/shuwei/dish/match/utils/RawMaterialsDialogUtils.kt new file mode 100644 index 0000000..b67e7fb --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/utils/RawMaterialsDialogUtils.kt @@ -0,0 +1,151 @@ +package com.shuwei.dish.match.utils + +import android.content.Context +import android.content.res.ColorStateList +import android.view.View +import androidx.core.content.ContextCompat +import com.google.android.material.chip.Chip +import com.google.android.material.chip.ChipGroup +import com.shuwei.dish.match.R +import com.shuwei.dish.match.dialog.CommonDialog +import com.shuwei.dish.match.entity.GoodsItem +import com.shuwei.dish.match.utils.ext.dp +import com.shuwei.dish.match.utils.ext.gone +import com.shuwei.dish.match.utils.ext.toast + +/** + * 构造用于效果测试的食材假数据 + * TODO: 联调完成后删除 + */ +fun buildMockGoodsList(): List = mutableListOf( + GoodsItem(goodsId = "1", goodsName = "土豆", rawMaterialsType = "土豆丝,土豆条,土豆片,土豆丁"), + GoodsItem(goodsId = "2", goodsName = "胡萝卜", rawMaterialsType = "胡萝卜丝,胡萝卜片,胡萝卜丁"), + GoodsItem(goodsId = "3", goodsName = "黄瓜", rawMaterialsType = "黄瓜丝,黄瓜片,黄瓜丁,黄瓜块"), + GoodsItem(goodsId = "4", goodsName = "洋葱", rawMaterialsType = "洋葱丝,洋葱丁,洋葱圈"), + GoodsItem(goodsId = "5", goodsName = "白菜", rawMaterialsType = "白菜丝,白菜块,白菜叶"), + GoodsItem(goodsId = "6", goodsName = "豆腐", rawMaterialsType = "豆腐块,豆腐丁"), + GoodsItem(goodsId = "7", goodsName = "猪肉", rawMaterialsType = "猪肉丝,猪肉片,猪肉丁,猪肉块"), + GoodsItem(goodsId = "8", goodsName = "鸡胸肉", rawMaterialsType = "鸡胸肉丝,鸡胸肉片,鸡胸肉丁"), + GoodsItem(goodsId = "9", goodsName = "牛肉", rawMaterialsType = "牛肉丝,牛肉片,牛肉块"), + GoodsItem(goodsId = "10", goodsName = "虾仁", rawMaterialsType = "整虾仁,切段虾仁"), + GoodsItem(goodsId = "11", goodsName = "茄子", rawMaterialsType = "茄子丝,茄子片,茄子块,茄子条"), + GoodsItem(goodsId = "12", goodsName = "青椒", rawMaterialsType = "青椒丝,青椒片,青椒块"), + GoodsItem(goodsId = "13", goodsName = "西红柿", rawMaterialsType = "西红柿片,西红柿块,西红柿丁"), + GoodsItem(goodsId = "14", goodsName = "芹菜", rawMaterialsType = "芹菜段,芹菜丝"), + GoodsItem(goodsId = "15", goodsName = "莲藕", rawMaterialsType = "莲藕片,莲藕丁,莲藕丝"), + // 以下5条 rawMaterialsType 为空,测试无净材数据场景 + GoodsItem(goodsId = "16", goodsName = "菠菜"), + GoodsItem(goodsId = "17", goodsName = "生菜"), + GoodsItem(goodsId = "18", goodsName = "香菇"), + GoodsItem(goodsId = "19", goodsName = "木耳"), + GoodsItem(goodsId = "20", goodsName = "豆芽"), +) + +/** + * 弹出净材种类单选弹窗 + * 确认选中后通过 onConfirmed 回调返回选中的种类文本,业务逻辑由调用方自行处理; + * 无净材数据时展示提示文字,点击确认直接关闭弹窗。 + * + * @param context 上下文 + * @param types 净材种类字符串(逗号分隔),为空时提示无数据 + * @param checkedType 弹窗打开时的默认选中项(与 types 中元素对比),null 表示无默认选中 + * @param onConfirmed 用户选中并确认后的回调,参数为选中的净材种类文本 + */ +fun showRawMaterialsDialog( + context: Context, + types: String?, + checkedType: String?, + onConfirmed: (selectedType: String) -> Unit +) { + val typeList = types?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() } ?: emptyList() + + val dialog = CommonDialog(context) + .setTitle("选择净材种类") + .setNegativeButton("取消") + + if (typeList.isEmpty()) { + // 无净材数据时仅展示提示文字 + dialog.setContent("无净材数据") + .setPositiveButton("确认") {} + } else { + // 构建单选 ChipGroup 作为自定义内容 + val chipGroup = buildSingleChoiceChipGroup(context, typeList, checkedType) + dialog.addContentView(chipGroup) { tvContent -> tvContent.gone() } + dialog.setPositiveButtonInterceptable("确认") { + val checkedId = chipGroup.checkedChipId + if (checkedId == View.NO_ID) { + context.toast("请选择净材种类") + // 返回 false 阻止弹窗关闭 + return@setPositiveButtonInterceptable false + } + onConfirmed(typeList[checkedId]) + true + } + } + + dialog.show() +} + +/** + * 构建单选样式的 ChipGroup + * + * @param context 上下文 + * @param typeList 选项文本列表,每一项作为一个 Chip 的文字 + * @param checkedType 默认选中项的文本,与 typeList 中元素相等的 Chip 会被勾选;为空则无默认选中 + * @return 配置完成的 ChipGroup,每个 Chip 的 id 即为其在 typeList 中的下标 + */ +fun buildSingleChoiceChipGroup( + context: Context, + typeList: List, + checkedType: String? +): ChipGroup { + return ChipGroup(context).apply { + isSingleSelection = true + isSelectionRequired = false + setPadding(5.dp, 40.dp, 5.dp, 40.dp) + chipSpacingVertical = 10.dp + chipSpacingHorizontal = 20.dp + typeList.forEachIndexed { index, typeName -> + val chip = Chip(context).apply { + id = index + text = typeName + textSize = 22f + isCheckable = true + chipStartPadding = 20.dp.toFloat() + chipEndPadding = 20.dp.toFloat() + chipMinHeight = (textSize + 15.dp * 2).toFloat() + // 选中/未选中态背景色:主题绿 / 浅灰 + chipBackgroundColor = ColorStateList( + arrayOf( + intArrayOf(android.R.attr.state_checked), + intArrayOf(-android.R.attr.state_checked) + ), + intArrayOf( + ContextCompat.getColor(context, R.color.dish_green), + ContextCompat.getColor(context, R.color.gray_eb) + ) + ) + // 选中/未选中态文字色:白 / 深灰 + setTextColor( + ColorStateList( + arrayOf( + intArrayOf(android.R.attr.state_checked), + intArrayOf(-android.R.attr.state_checked) + ), + intArrayOf( + ContextCompat.getColor(context, R.color.white), + ContextCompat.getColor(context, R.color.black333) + ) + ) + ) + // 隐藏默认的左侧勾选图标,让填充色作为唯一选中反馈 + isCheckedIconVisible = false + // 去除描边 + chipStrokeWidth = 0f + // 若当前项已有选中值则默认勾选对应选项 + isChecked = (typeName == checkedType) + } + addView(chip) + } + } +} diff --git a/app/src/main/res/drawable/setting_border.xml b/app/src/main/res/drawable/setting_border.xml index b744f3a..10444d4 100644 --- a/app/src/main/res/drawable/setting_border.xml +++ b/app/src/main/res/drawable/setting_border.xml @@ -3,4 +3,5 @@ android:shape="rectangle"> + \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_food.xml b/app/src/main/res/layout/list_item_food.xml index dce665b..4f97a99 100644 --- a/app/src/main/res/layout/list_item_food.xml +++ b/app/src/main/res/layout/list_item_food.xml @@ -1,35 +1,55 @@ - + android:layout_height="100dp" + android:layout_margin="15dp"> + + + android:gravity="center" + android:maxLines="1" + android:textColor="@color/black999" + android:textSize="18sp" + android:visibility="gone" + android:layout_marginBottom="5dp" + app:layout_constraintHorizontal_chainStyle="packed" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@id/tvFoodScore" + app:layout_constraintStart_toStartOf="parent" + tools:text="切丝" /> + android:maxLines="1" + android:textColor="@color/black666" + android:layout_marginStart="10dp" + android:textSize="16sp" + android:visibility="gone" + app:layout_constraintTop_toTopOf="@id/tvRawMaterialsType" + app:layout_constraintBottom_toBottomOf="@id/tvRawMaterialsType" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/tvRawMaterialsType" + tools:text="100%" /> - - \ No newline at end of file + \ No newline at end of file