refactor: 提取净材种类弹窗为工具类并统一各页面选择逻辑
- 新增 RawMaterialsDialogUtils,包含 showRawMaterialsDialog、 buildSingleChoiceChipGroup、buildMockGoodsList 三个顶层函数 - FoodAdapter/list_item_food 新增净材种类选中结果展示 - VectorCollectionFragment、FoodRecognizeActivity、FoodSearchDialog 的食材点击均改为弹出净材种类单选弹窗后再确认选中 - FoodSearchDialog 空布局补充点击加载测试数据 - setting_border 新增圆角
This commit is contained in:
@@ -48,6 +48,14 @@ class FoodAdapter(list: MutableList<GoodsItem>) :
|
|||||||
it.text = foodScoreText
|
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(
|
override fun onCreateViewHolder(
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import androidx.lifecycle.repeatOnLifecycle
|
|||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.chad.library.adapter4.util.setOnDebouncedItemClick
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.shuwei.dish.match.R
|
import com.shuwei.dish.match.R
|
||||||
import com.shuwei.dish.match.adapter.FoodAdapter
|
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.entity.GoodsItem
|
||||||
import com.shuwei.dish.match.net.UiState
|
import com.shuwei.dish.match.net.UiState
|
||||||
import com.shuwei.dish.match.utils.KeyboardUtil
|
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.addOnActionSearchListener
|
||||||
import com.shuwei.dish.match.utils.ext.toast
|
import com.shuwei.dish.match.utils.ext.toast
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -50,36 +53,25 @@ class FoodSearchDialog(
|
|||||||
|
|
||||||
private val binding = DialogFoodSearchBinding.inflate(LayoutInflater.from(activity))
|
private val binding = DialogFoodSearchBinding.inflate(LayoutInflater.from(activity))
|
||||||
private val list = mutableListOf<GoodsItem>()
|
private val list = mutableListOf<GoodsItem>()
|
||||||
// .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 {
|
private val adapter = FoodAdapter(list).apply {
|
||||||
isStateViewEnable = true
|
isStateViewEnable = true
|
||||||
setOnItemClickListener { _, _, position ->
|
setOnDebouncedItemClick { _, _, position ->
|
||||||
list[position].isClicked = true
|
val item = list[position]
|
||||||
notifyItemChanged(position)
|
// 弹出净材种类单选弹窗,确认后再回调并关闭弹窗
|
||||||
binding.root.postDelayed({
|
showRawMaterialsDialog(
|
||||||
onItemSelected(list[position])
|
context = activity,
|
||||||
dismiss()
|
types = item.rawMaterialsTypes,
|
||||||
}, 300)
|
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)
|
binding.recyclerView.layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.VERTICAL, false)
|
||||||
if (pageNo == 1) list.clear()
|
if (pageNo == 1) list.clear()
|
||||||
|
// 将接口返回的 rawMaterialsType(全部种类)移存到 rawMaterialsTypes,供弹窗使用
|
||||||
|
records.forEach {
|
||||||
|
it.rawMaterialsTypes = it.rawMaterialsType
|
||||||
|
it.rawMaterialsType = null
|
||||||
|
}
|
||||||
list.addAll(records)
|
list.addAll(records)
|
||||||
adapter.notifyDataSetChanged()
|
adapter.notifyDataSetChanged()
|
||||||
val isLoadMoreEnable = records.size >= PAGE_SIZE
|
val isLoadMoreEnable = records.size >= PAGE_SIZE
|
||||||
@@ -269,16 +266,19 @@ class FoodSearchDialog(
|
|||||||
false
|
false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
emptyViewBinding!!.tvContent.text = "暂无数据"
|
emptyViewBinding?.let {
|
||||||
emptyViewBinding!!.tvSubContent.text = "未查询到食材信息,请联系管理员添加"
|
it.tvContent.text = "暂无数据"
|
||||||
emptyViewBinding!!.root.layoutParams = FrameLayout.LayoutParams(
|
it.tvSubContent.text = "未查询到食材信息,请联系管理员添加"
|
||||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
it.root.setOnClickListener { loadGoodsList(buildMockGoodsList().toMutableList()) }
|
||||||
FrameLayout.LayoutParams.MATCH_PARENT
|
it.root.layoutParams = FrameLayout.LayoutParams(
|
||||||
)
|
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||||
binding.recyclerView.layoutManager = LinearLayoutManager(activity)
|
FrameLayout.LayoutParams.MATCH_PARENT
|
||||||
binding.recyclerView.post {
|
)
|
||||||
emptyViewBinding!!.root.minimumHeight = binding.recyclerView.height
|
binding.recyclerView.layoutManager = LinearLayoutManager(activity)
|
||||||
adapter.stateView = emptyViewBinding!!.root
|
binding.recyclerView.post {
|
||||||
|
it.root.minimumHeight = binding.recyclerView.height
|
||||||
|
adapter.stateView = it.root
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import com.shuwei.dish.match.net.UiState
|
|||||||
import com.shuwei.dish.match.objbox.FoodModule
|
import com.shuwei.dish.match.objbox.FoodModule
|
||||||
import com.shuwei.dish.match.utils.ext.dp
|
import com.shuwei.dish.match.utils.ext.dp
|
||||||
import com.shuwei.dish.match.utils.ext.toast
|
import com.shuwei.dish.match.utils.ext.toast
|
||||||
|
import com.shuwei.dish.match.utils.showRawMaterialsDialog
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,12 +123,21 @@ class FoodRecognizeActivity : BaseActivity() {
|
|||||||
|
|
||||||
private val adapter = FoodAdapter(list).apply {
|
private val adapter = FoodAdapter(list).apply {
|
||||||
setOnItemClickListener { _, _, position ->
|
setOnItemClickListener { _, _, position ->
|
||||||
list.forEachIndexed { i, item -> item.isClicked = (i == position) }
|
val item = list[position]
|
||||||
// 点击选择时记录当前秤重量
|
// 弹出净材种类单选弹窗,确认后再选中该食材
|
||||||
list[position].useWeight = currentWeight
|
showRawMaterialsDialog(
|
||||||
notifyDataSetChanged()
|
context = this@FoodRecognizeActivity,
|
||||||
selectedPosition = position
|
types = item.rawMaterialsTypes,
|
||||||
binding.tvSelectedFood.text = list[position].goodsName ?: "-"
|
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 {
|
goodsList.forEach {
|
||||||
val food = nameScoreMap[it.goodsName]
|
val food = nameScoreMap[it.goodsName]
|
||||||
val foodScore = ((1 - (food?.score ?: 0.0)) * 10000).toInt()
|
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 })
|
list.add(it.also { it.foodScore = foodScore })
|
||||||
}
|
}
|
||||||
adapter.notifyDataSetChanged()
|
adapter.notifyDataSetChanged()
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package com.shuwei.dish.match.ui.fragment
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -12,10 +11,7 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.view.inputmethod.InputMethodManager
|
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.camera.view.PreviewView
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.view.doOnLayout
|
import androidx.core.view.doOnLayout
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.lifecycle.lifecycleScope
|
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.FragmentVectorCollectionBinding
|
||||||
import com.shuwei.dish.match.databinding.LayoutCameraPreviewBinding
|
import com.shuwei.dish.match.databinding.LayoutCameraPreviewBinding
|
||||||
import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding
|
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.dialog.Loading
|
||||||
import com.shuwei.dish.match.entity.FoodCollectionBean
|
import com.shuwei.dish.match.entity.FoodCollectionBean
|
||||||
import com.shuwei.dish.match.entity.GoodsItem
|
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.ImageUploader
|
||||||
import com.shuwei.dish.match.utils.ImageUtil
|
import com.shuwei.dish.match.utils.ImageUtil
|
||||||
import com.shuwei.dish.match.utils.LogSaveUtil
|
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.clickWithDebounce
|
||||||
import com.shuwei.dish.match.utils.ext.dp
|
import com.shuwei.dish.match.utils.ext.dp
|
||||||
import com.shuwei.dish.match.utils.ext.gone
|
import com.shuwei.dish.match.utils.ext.gone
|
||||||
@@ -81,9 +79,27 @@ class VectorCollectionFragment : BaseFragment<FragmentVectorCollectionBinding>()
|
|||||||
isStateViewEnable = true
|
isStateViewEnable = true
|
||||||
setOnDebouncedItemClick { _, _, position ->
|
setOnDebouncedItemClick { _, _, position ->
|
||||||
val item = searchList[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<FragmentVectorCollectionBinding>()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造用于效果测试的食材假数据
|
|
||||||
* TODO: 联调完成后删除
|
|
||||||
*/
|
|
||||||
private fun buildMockGoodsList(): List<GoodsItem> = 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<String>,
|
|
||||||
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
|
var clickIndex = -1
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
|||||||
@@ -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<GoodsItem> = 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<String>,
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,4 +3,5 @@
|
|||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
<solid android:color="#FFF4FAFF"/>
|
<solid android:color="#FFF4FAFF"/>
|
||||||
<stroke android:width="2dp" android:color="@color/white"/>
|
<stroke android:width="2dp" android:color="@color/white"/>
|
||||||
|
<corners android:radius="8dp"/>
|
||||||
</shape>
|
</shape>
|
||||||
@@ -1,35 +1,55 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout 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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="100dp"
|
||||||
|
android:layout_margin="15dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvGoodsInfo"
|
android:id="@+id/tvGoodsInfo"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="100dp"
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/shape_white_12_corners"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:paddingHorizontal="5dp"
|
||||||
android:textColor="@color/black666"
|
android:textColor="@color/black666"
|
||||||
android:textSize="30sp"
|
android:textSize="30sp"
|
||||||
android:paddingHorizontal="5dp"
|
tools:text="椒盐" />
|
||||||
android:gravity="center"
|
|
||||||
android:layout_margin="15dp"
|
<TextView
|
||||||
tools:text="椒盐"
|
android:id="@+id/tvRawMaterialsType"
|
||||||
android:maxLines="1"
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:background="@drawable/shape_white_12_corners"/>
|
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="切丝" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvFoodScore"
|
android:id="@+id/tvFoodScore"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textColor="@color/black666"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:layout_marginBottom="20dp"
|
|
||||||
android:layout_gravity="bottom|center_horizontal"
|
|
||||||
tools:text="100%"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:visibility="gone"/>
|
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%" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</FrameLayout>
|
|
||||||
Reference in New Issue
Block a user