feat(ui): 新增餐品列表和菜品包装页面

- 创建了 MealListActivity 餐品列表页面,用于展示可选的餐品
- 实现了餐品列表的 RecyclerView 展示和点击跳转功能
- 添加了 PackActivity 菜品包装页面,支持食材构成管理
- 集成了相机预览功能用于食材识别
- 实现了重量识别和食材添加功能
- 添加了侧滑删除菜单和食材重量管理
- 创建了 MealListAdapter 适配器用于餐品列表展示
This commit is contained in:
2026-06-02 11:14:48 +08:00
parent 48d331f05d
commit 9801534aad
5 changed files with 1013 additions and 0 deletions
@@ -0,0 +1,36 @@
package com.shuwei.dish.match.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.shuwei.dish.match.databinding.ListItemFood2Binding
import com.shuwei.dish.match.model.NutFoodOptionVO
/**
* 餐品列表适配器
*/
class MealListAdapter(list: MutableList<NutFoodOptionVO>) :
BaseQuickAdapter<NutFoodOptionVO, MealListAdapter.VH>(list) {
inner class VH(var binding: ListItemFood2Binding) : QuickViewHolder(binding.root)
override fun onBindViewHolder(
holder: VH,
position: Int,
item: NutFoodOptionVO?
) {
holder.binding.tvDishInfo.text = item?.foodName
}
override fun onCreateViewHolder(
context: Context,
parent: ViewGroup,
viewType: Int
): VH {
val inflater = LayoutInflater.from(context)
val binding = ListItemFood2Binding.inflate(inflater, parent, false)
return VH(binding)
}
}
@@ -0,0 +1,98 @@
package com.shuwei.dish.match.ui
import android.content.Intent
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.shuwei.dish.match.adapter.MealListAdapter
import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.databinding.ActivityMealListBinding
import com.shuwei.dish.match.model.NutFoodOptionVO
import com.shuwei.dish.match.net.NetViewModelV2
import com.shuwei.dish.match.net.UiState
import com.shuwei.dish.match.utils.ext.gone
import com.shuwei.dish.match.utils.ext.toast
import com.shuwei.dish.match.utils.ext.visible
/**
* 餐品列表页面
*/
class MealListActivity : BaseActivity() {
companion object {
const val MEAL_ITEM = "mealItem"
}
private val binding by lazy { ActivityMealListBinding.inflate(layoutInflater) }
private val viewModelV2 by lazy { NetViewModelV2() }
private val mealList = mutableListOf<NutFoodOptionVO>()
private val mealAdapter by lazy { MealListAdapter(mealList) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
setHeaderBackground()
setTitleBar(titleBarAction = {
it.visible()
}, titleAction = {
it.text = "餐品列表"
}, rightIconAction = {
it.gone()
}, backAction = {
it.setOnClickListener {
finish()
}
})
initRecyclerView()
loadMealList()
}
/**
* 初始化 RecyclerView
*/
private fun initRecyclerView() {
binding.rvMealList.layoutManager = LinearLayoutManager(this)
binding.rvMealList.adapter = mealAdapter
mealAdapter.setOnItemClickListener { _, _, position ->
val item = mealList.getOrNull(position) ?: return@setOnItemClickListener
val intent = Intent(this, PackActivity::class.java).apply {
putExtra(PackActivity.MEAL_ITEM, item)
}
startActivity(intent)
}
}
/**
* 加载餐品列表
*/
private fun loadMealList() {
viewModelV2.getFoodOptionsWithCallback(
keyword = null,
pageNum = 1,
pageSize = 100,
onLoading = {
showLoading("加载中……")
},
onResult = { state ->
dismissLoading()
when (state) {
is UiState.Success -> {
val records = state.data?.records
if (records.isNullOrEmpty()) {
toast("暂无数据")
return@getFoodOptionsWithCallback
}
mealList.clear()
mealList.addAll(records)
mealAdapter.notifyDataSetChanged()
}
is UiState.Error -> {
toast(state.msg)
}
else -> {}
}
}
)
}
}
@@ -0,0 +1,607 @@
package com.shuwei.dish.match.ui
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.ViewGroup
import androidx.activity.addCallback
import androidx.core.content.IntentCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.FoodMaterialAdapter
import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.databinding.ActivityPackBinding
import com.shuwei.dish.match.databinding.LayoutCameraPreviewBinding
import com.shuwei.dish.match.dialog.CommonDialog
import com.shuwei.dish.match.model.CookFoodDTO
import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.model.FoodRecord
import com.shuwei.dish.match.model.GoodsItem
import com.shuwei.dish.match.model.NutFoodOptionVO
import com.shuwei.dish.match.net.UiState
import com.shuwei.dish.match.objbox.FoodModule
import com.shuwei.dish.match.utils.AddressUtil
import com.shuwei.dish.match.utils.CameraUtils
import com.shuwei.dish.match.utils.ImageUtil
import com.shuwei.dish.match.utils.SpTool
import com.shuwei.dish.match.utils.WeightUtil
import com.shuwei.dish.match.utils.ext.addOnActionSearchListener
import com.shuwei.dish.match.utils.ext.clickWithDebounce
import com.shuwei.dish.match.utils.ext.dp
import com.shuwei.dish.match.utils.ext.gone
import com.shuwei.dish.match.utils.ext.startActivity
import com.shuwei.dish.match.utils.ext.toJsonString
import com.shuwei.dish.match.utils.ext.toast
import com.shuwei.dish.match.utils.ext.visible
import com.yanzhenjie.recyclerview.SwipeMenuItem
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.Serializable
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.math.abs
import com.shuwei.dish.match.model.NutFoodComposition
import com.shuwei.dish.match.net.NetViewModelV2
import com.shuwei.dish.match.utils.ext.roundedOneDecimalPlace
@SuppressLint("NotifyDataSetChanged")
class PackActivity : BaseActivity() {
companion object {
const val TAG = "PackActivity"
const val MEAL_ITEM = "mealItem"
const val PAGE_FROM = "pageFrom"
const val HOME = "home"
const val WEIGHT_CHANGE_VALUE = 5
const val WEIGHT_RECOGNIZE_VALUE = 10
/** 主材数量上限 */
const val MATERIAL_COUNT = 3
}
private val binding by lazy { ActivityPackBinding.inflate(layoutInflater) }
private val cameraUtils: CameraUtils by lazy { CameraUtils(this) }
private var mealItem: NutFoodOptionVO? = null
/** 来源页面标识,HOME 时返回跳转 SamplingListActivity */
private var pageFrom: String? = null
private var goodsList: MutableList<CookFoodGoodsEntity>? = null
private val viewModelV2 by lazy { NetViewModelV2() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
setHeaderBackground()
pageFrom = intent.getStringExtra(PAGE_FROM)
mealItem = IntentCompat.getParcelableExtra(intent, MEAL_ITEM, NutFoodOptionVO::class.java)
mealItem?.let {
binding.etInputDish.run {
setText(it.foodName)
tag = it.foodName
}
}
val isSamplingMode = SpTool.cookMode == 1
val titleText = if (isSamplingMode) "菜品信息采集" else "菜品净菜包装"
setTitleBar(titleBarAction = {
it.visible()
}, titleAction = {
it.text = titleText
}, rightIconAction = {
it.gone()
}, backAction = {
it.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
})
// 不可编辑,仅展示菜名
binding.etInputDish.isFocusable = false
binding.etInputDish.isFocusableInTouchMode = false
binding.ivDishSearch.gone()
addViewClickListener()
addBackKeyListener()
initCamera(binding.flCameraContainer)
initRecyclerView()
initObserver()
// 根据传入的餐品数据加载食材列表
mealItem?.let {
val compositions = it.compositions
if (!compositions.isNullOrEmpty()) {
loadDishCompositions(compositions)
} else {
toast("该餐品无食材构成信息")
}
}
}
private var currentWeight = 0.0
private fun addViewClickListener() {
// ========== 重量识别相关功能已注释 ==========
WeightUtil.addWeightListener(
weightKey = TAG,
getWeight = { address, state, weight ->
if (address == AddressUtil.ONE && state == WeightUtil.STATE_STABLE) {
runOnUiThread {
currentWeight = weight - initialWeight
binding.tvShowWeight.run {
if (tag != currentWeight) {
// text = "称重:${netWeight}g"
val weightText = "${currentWeight.roundedOneDecimalPlace()}g"
text = weightText
tag = currentWeight
}
}
//recognizeFood(netWeight)
}
}
})
if (SpTool.cookMode == 1) {
// 采集模式:绑定搜索栏交互
binding.ivDishSearch.visible()
binding.etInputDish.isFocusable = true
binding.etInputDish.isFocusableInTouchMode = true
binding.ivDishSearch.setOnClickListener { jumpSearch() }
binding.etInputDish.addOnActionSearchListener { jumpSearch() }
}
// binding.btnCook.clickWithDebounce {
// if (SpTool.cookMode == 1) {
// // 采集模式:从输入框创建 FoodRecord
// val showFoodName = binding.etInputDish.text.toString().trim()
// if (showFoodName.isBlank()) {
// toast("菜品名称为空")
// return@clickWithDebounce
// }
// if (list.isEmpty()) {
// toast("菜品构成信息未设置")
// return@clickWithDebounce
// }
// val foodRecord = FoodRecord().apply {
// foodId = mealItem?.foodId?.toString()
// foodName = showFoodName
// cookMode = 1
// }
// Log.d(TAG, "goToSubmit: goodsList:${list.toJsonString()}")
// val submitList = list.map { it.toCookFoodGoodsEntity() }.toMutableList()
// startActivity<SubmitFoodActivity> {
// putExtra(SubmitFoodActivity.GOODS_LIST, submitList as Serializable)
// putExtra(SubmitFoodActivity.FOOD_ITEM, foodRecord as Serializable)
// }
// } else {
// // 包装模式:检查食材是否全部设置完成
// val count = list.count { it.isSetFinished.not() }
// if (count > 0) {
// showRemindDialog()
// return@clickWithDebounce
// }
// openSubmitPage()
// }
// }
binding.ivClearWeight.clickWithDebounce {
initialWeight = 0.0
WeightUtil.tareTwo(1)
}
binding.ivAddWeight.clickWithDebounce {
if(currentWeight <= 0.0) {
toast("当前秤重量无效")
return@clickWithDebounce
}
list.getOrNull(clickIndex)?.let {
it.useWeight = currentWeight
it.isSetFinished = true
materialAdapter.notifyItemChanged(clickIndex)
}
}
binding.btnGetCode.clickWithDebounce {
val item = list.getOrNull(clickIndex)?:return@clickWithDebounce
//viewModelV2.getCode
}
binding.btnSubmit.clickWithDebounce {
}
}
// private val isTakingPhoto = AtomicBoolean(false)
// ========== 重量识别相关变量已注释 ==========
// private var showRecognizePage = false
// private var pageVisible = true
// private var manualCancelFlag = false
// private var lastPhotoUri: Uri? = null
// private var lastWeight = 0.0
// private var currentWeight = 0.0
private var initialWeight = SpTool.configWeight.toDouble()
// private fun recognizeFood(weight: Double) {
// // 重量识别逻辑已注释
// }
/**
* 重新计算 list 中非接口数据(isOriginalData=false)的 materialType
*
* 规则:
* - 接口数据主材数量 n = list 中 isOriginalData=true 且 materialType=1 的数量
* - 有接口数据且 MATERIAL_COUNT - n <= 0:所有非接口数据均为辅材(2)
* - 无接口数据,或 MATERIAL_COUNT - n > 0m = MATERIAL_COUNT - n
* 非接口数据按 useWeight 从大到小排序,前 m 个为主材(1),其余为辅材(2)
*/
private fun updateMaterialTypes() {
val hasOriginalData = list.any { it.isOriginalData }
// 接口数据中主材数量
val n = list.count { it.isOriginalData && it.materialType == 1 }
// 待分配的非接口数据
val nonOriginalList = list.filter { !it.isOriginalData }
if (hasOriginalData && MATERIAL_COUNT - n <= 0) {
// 主材已满,所有非接口数据均为辅材
nonOriginalList.forEach { it.materialType = 2 }
} else {
// 剩余可分配主材名额
val m = MATERIAL_COUNT - n
// 按 useWeight 从大到小排序
val sorted = nonOriginalList.sortedByDescending { it.useWeight ?: 0.0 }
sorted.forEachIndexed { index, item ->
item.materialType = if (index < m) 1 else 2
}
}
materialAdapter.notifyDataSetChanged()
}
private fun openSubmitPage() {
getGoodsList()
if (goodsList.isNullOrEmpty()) {
toast("无菜品构成信息")
return
}
Log.d(TAG, "goToSubmit: goodsList:${goodsList?.toJsonString()}")
startActivity<SubmitFoodActivity> {
putExtra(SubmitFoodActivity.GOODS_LIST, goodsList as Serializable)
putExtra(SubmitFoodActivity.FOOD_ITEM, FoodRecord().apply {
foodId = mealItem?.foodId?.toString()
foodName = mealItem?.foodName
})
}
}
private fun getGoodsList() {
if (goodsList == null) {
goodsList = mutableListOf()
} else {
goodsList!!.clear()
}
// 将 Goods Item 转换为 CookFoodGoodsEntity 并补充菜品相关字段
list.filter { it.isSetFinished }.forEach {
goodsList?.add(it.toCookFoodGoodsEntity().apply {
foodId = mealItem?.foodId?.toString()
})
}
}
private fun showRemindDialog() {
CommonDialog(this)
.setTitle(getString(R.string.food_remind_01))
.setContent(getString(R.string.food_remind_02))
.setNegativeButton("返回调整")
.setPositiveButton("确认无误") { openSubmitPage() }
.setOnDismissCallback { hideStatusBar() }
.show()
}
private fun initObserver() {
lifecycleScope.launch {
netViewModel.foodDetailState.collect { state ->
when (state) {
is UiState.Success -> {
val detail = state.data
if (detail == null) {
toast("查询菜品信息为空")
return@collect
}
loadDishDetail(detail)
}
is UiState.Error -> toast(state.msg)
else -> {}
}
}
}
}
// ========== getDishDetail 方法已删除,不再通过接口查询 ==========
private fun loadDishDetail(detail: CookFoodDTO) {
val voList = detail.foodConstituteList
if (voList.isNullOrEmpty()) {
return
}
// 筛选出主材和辅材,转换为 Goods ItemuseWeight 置零)
val tempData = voList.filter { it.materialType == 1 || it.materialType == 2 }
.map { it.toGoodsItem().also { item -> item.useWeight = 0.0 } }
// 直接替换 adapter.items
list.clear()
list.addAll(tempData)
materialAdapter.items = list
materialAdapter.notifyDataSetChanged()
firstReqSize = list.size
}
/**
* 加载餐品食材构成(从 NutFoodOptionVO.compositions 解析)
*/
private fun loadDishCompositions(compositions: List<NutFoodComposition>) {
// 将 NutFoodComposition 转换为 Goods Item
val tempData = compositions.map { comp ->
GoodsItem().apply {
goodsId = comp.materId?.toString() ?: ""
goodsName = comp.ingredientName ?: ""
materialType = if (comp.isMain == 1) 1 else 2
useWeight = 0.0
isOriginalData = true
isSetFinished = false
}
}.toMutableList()
list.clear()
list.addAll(tempData)
materialAdapter.items = list
materialAdapter.notifyDataSetChanged()
firstReqSize = list.size
}
private var firstReqSize = 0
private val list = mutableListOf<GoodsItem>()
private var clickIndex = -1
private val materialAdapter by lazy {
FoodMaterialAdapter(list).apply {
onItemClick = { positon ->
this@PackActivity.clickIndex = positon
list.forEachIndexed { index, entity ->
entity.isClicked = index == positon
}
notifyDataSetChanged()
}
addOnItemChildClickListener(R.id.ivClearIcon) { _, _, position ->
val item = list.getOrNull(position) ?: return@addOnItemChildClickListener
Log.d(TAG, "onFoodItemClick: ${item.toJsonString()}")
if (item.isOriginalData) {
// 无重量信息时转为 item 整体点击(触发选中效果)
if (item.useWeight == null || item.useWeight == 0.0) {
onItemClick?.invoke(position)
return@addOnItemChildClickListener
}
deleteRemindDialog("清除确认", "确定清除食材「${item.goodsName}」添加的重量吗?") {
item.run {
isNewDishType = false
useWeight = 0.0
isSetFinished = false
}
notifyItemChanged(position)
}
return@addOnItemChildClickListener
}
deleteRemindDialog("删除确认", "确定删除食材「${item.goodsName}」吗?") {
removeFood(position)
}
}
}
}
/**
* RecyclerView初始化
*/
@SuppressLint("ClickableViewAccessibility")
private fun initRecyclerView() {
binding.rvMaterialList.let {
it.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
// 仅 isNewDishType=true 的 item 显示侧滑删除菜单(必须在 setAdapter 之前调用)
it.setSwipeMenuCreator { _, rightMenu, position ->
if (list.getOrNull(position)?.isNewDishType == true) {
rightMenu.addMenuItem(buildDeleteMenuItem())
}
}
// 点击侧滑菜单项:先关闭菜单,再弹窗确认删除
it.setOnItemMenuClickListener { menuBridge, position ->
menuBridge.closeMenu()
val item = list.getOrNull(position) ?: return@setOnItemMenuClickListener
deleteRemindDialog("删除确认", "确定删除食材「${item.goodsName}」吗?") {
removeFood(position)
}
}
// item 点击事件
it.setOnItemClickListener { _, position ->
materialAdapter.onItemClick?.invoke(position)
}
handleRvTouchHideKeyboard()
it.adapter = materialAdapter
}
}
/**
* 删除提醒
*/
private fun deleteRemindDialog(title: String, content: String, action: () -> Unit) {
CommonDialog(this@PackActivity)
.setTitle(title)
.setContent(content)
.setNegativeButton("取消")
.setPositiveButton("确认") {
action()
}.show()
}
/**
* 移除菜品
*/
private fun removeFood(position: Int) {
materialAdapter.removeAt(position)
toast("已删除")
updateMaterialTypes()
}
/**
* 构建侧滑删除菜单项
*/
private fun buildDeleteMenuItem(): SwipeMenuItem {
return SwipeMenuItem(this).apply {
setImage(R.drawable.ic_trash_white)
setBackground(R.drawable.bg_swipe_delete)
width = 160.dp
height = ViewGroup.LayoutParams.MATCH_PARENT
}
}
override fun onResume() {
super.onResume()
// pageVisible = true
// showRecognizePage = false
cameraUtils.bind()
}
override fun onPause() {
super.onPause()
// pageVisible = false
cameraUtils.unbind()
}
/**
* 初始化相机并绑定预览容器
* @param container 相机预览容器
*/
fun initCamera(container: ViewGroup) {
cameraUtils.initCamera()
val previewBinding = LayoutCameraPreviewBinding.inflate(layoutInflater, container)
cameraUtils.setPreviewController(previewBinding.previewView)
}
/**
* 注册返回键监听,替代已废弃的 onBackPressed()
*/
private fun addBackKeyListener() {
onBackPressedDispatcher.addCallback(this) {
if (SpTool.cookMode == 1) {
// 采集模式:有新增食材时提示保存
if (list.isEmpty().not()) {
saveDataRemindDialog()
return@addCallback
}
if (pageFrom == HOME) {
startActivity<SamplingModeActivity>()
}
} else {
// 包装模式:有未保存数据时提示
val count = list.count { !it.isOriginalData }
if (count > 0) {
saveDataRemindDialog()
return@addCallback
}
}
finish()
}
}
/**
* 未保存数据提醒
*/
private fun saveDataRemindDialog() {
CommonDialog(this)
.setTitle("返回提示")
.setContent("您好,当前页面存在未保存的数据,\n确认返回吗?")
.setNegativeButton("取消")
.setPositiveButton("确认") {
if (SpTool.cookMode == 1 && pageFrom == HOME) {
startActivity<SamplingModeActivity>()
}
finish()
}
.setOnDismissCallback { hideStatusBar() }
.show()
}
// ========== 拍照回调相关功能已注释 ==========
// private val cameraSuccessCallback: (Uri) -> Unit = { uri ->
// // 拍照成功回调已注释
// }
// private var notRecognizeDialog: CommonDialog? = null
// private fun showNotRecognizeDialog() {
// // 未识别弹窗已注释
// }
// private val cameraFailureCallback: (String) -> Unit = { errMsg ->
// // 拍照失败回调已注释
// }
// private fun loadRecognizeResultPage(nameScoreList: List<FoodModule.IdNameScore>) {
// // 识别结果页加载已注释
// }
/**
* 跳转食物搜索页(采集模式专用)
*/
private fun jumpSearch() {
val searchContent = binding.etInputDish.text.toString().trim()
if (searchContent.isBlank()) {
toast(binding.etInputDish.hint.toString())
return
}
val launchIntent = Intent(this, FoodSearchActivity::class.java).apply {
putExtra(FoodSearchActivity.FOOD_NAME, searchContent)
}
startActivity(launchIntent) { resultIntent ->
if (resultIntent == null) return@startActivity
val food = IntentCompat.getSerializableExtra(resultIntent, FoodSearchActivity.FOOD_ITEM, FoodRecord::class.java)
Log.d(TAG, "jumpSearch: record=$food")
if (food == null) return@startActivity
binding.etInputDish.run {
setText(food.foodName)
tag = food.foodName
setSelection(text.length)
}
}
hideKeyboard()
}
private fun handleRvTouchHideKeyboard() {
val gestureDetector = GestureDetector(this, object : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapUp(e: MotionEvent): Boolean {
hideKeyboard()
return false
}
})
binding.rvMaterialList.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() {
private var startX = 0f
private var startY = 0f
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
when (e.action) {
MotionEvent.ACTION_DOWN -> {
startX = e.x
startY = e.y
}
MotionEvent.ACTION_MOVE -> {
val dx = abs(e.x - startX)
val dy = abs(e.y - startY)
if (dx > dy && dx > 10) {
hideKeyboard()
}
}
}
gestureDetector.onTouchEvent(e)
return false
}
})
}
}
@@ -0,0 +1,41 @@
<?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"
android:orientation="vertical"
tools:background="@drawable/bg_other_page">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="30dp"
android:layout_weight="1"
android:background="@drawable/shape_white_30_corners"
android:orientation="vertical">
<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:padding="10dp"
android:text="餐品列表"
android:textColor="@color/black666"
android:textSize="28sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvMealList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="20dp"
android:overScrollMode="never"
android:scrollbars="vertical"
tools:itemCount="3"
tools:listitem="@layout/list_item_food2" />
</LinearLayout>
</LinearLayout>
+231
View File
@@ -0,0 +1,231 @@
<?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"
android:orientation="vertical"
tools:background="@drawable/bg_other_page">
<LinearLayout
android:id="@+id/llSearchBar"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_margin="30dp"
android:background="@drawable/shape_white_30_corners"
android:gravity="center_vertical"
android:orientation="horizontal">
<EditText
android:id="@+id/etInputDish"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_marginHorizontal="28dp"
android:layout_weight="1"
android:autofillHints=""
android:background="@color/white"
android:ellipsize="end"
android:gravity="center"
android:hint="@string/dish_search_hint"
android:imeOptions="actionSearch"
android:inputType="text"
android:maxLines="1"
android:paddingStart="3dp"
android:paddingEnd="3dp"
android:textColor="@color/black333"
android:textColorHint="@color/gray_c8"
android:textSize="40sp"
tools:ignore="TextFields" />
<ImageView
android:id="@+id/ivDishSearch"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:src="@drawable/ic_search_gray"
android:visibility="gone"
tools:ignore="ContentDescription" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="30dp"
android:background="@drawable/shape_white_30_corners"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="90dp"
android:gravity="center_vertical">
<TextView
android:id="@+id/tvGoodsName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:textColor="@color/black"
android:textSize="30sp"
android:textStyle="bold"
tools:text="豆腐" />
<TextView
android:id="@+id/tvMaterialType"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:textColor="@color/black999"
android:textSize="26sp"
tools:text="主材" />
<TextView
android:id="@+id/btnGetCode"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginEnd="30dp"
android:paddingHorizontal="20dp"
android:text="获取溯源码"
android:textSize="18sp"
android:gravity="center"
android:textColor="@color/dish_green"
android:background="@drawable/ripple_effect_light"/>
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginHorizontal="30dp"
app:dividerColor="@color/gray_eb" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="160dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:text="用量(克)"
android:textColor="@color/black666"
android:textSize="26sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvShowWeight"
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_marginStart="30dp"
android:layout_weight="1"
android:background="@drawable/shape_white_f6_10_corners"
android:gravity="center"
android:text="-"
android:textColor="@color/gray_d6"
android:textSize="50sp" />
<ImageView
android:id="@+id/ivClearWeight"
android:layout_width="90dp"
android:layout_height="90dp"
android:layout_marginStart="30dp"
android:src="@drawable/ic_weight_clear" />
<ImageView
android:id="@+id/ivAddWeight"
android:layout_width="90dp"
android:layout_height="90dp"
android:layout_marginStart="30dp"
android:layout_marginEnd="30dp"
android:src="@drawable/ic_weight_add" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="30dp"
android:text="在下方列表中选择食材计重"
android:textColor="@color/black999"
android:textSize="26sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginHorizontal="30dp"
android:layout_marginTop="30dp"
android:layout_weight="1"
android:background="@drawable/shape_white_30_corners"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginVertical="30dp"
android:layout_marginStart="20dp"
android:padding="10dp"
android:text="@string/dish_composition"
android:textColor="@color/black666"
android:textSize="28sp" />
<!-- 重量显示已注释,重量识别功能整体已移除 -->
<!--
<TextView
android:id="@+id/tvShowWeight"
android:layout_width="wrap_content"
android:layout_height="80dp"
android:layout_gravity="end|center_vertical"
android:paddingHorizontal="30dp"
android:gravity="center_vertical"
android:maxLines="1"
android:textColor="@color/black666"
android:textSize="28sp" />
-->
<FrameLayout
android:id="@+id/flCameraContainer"
android:layout_width="1dp"
android:layout_height="1dp" />
</FrameLayout>
<com.yanzhenjie.recyclerview.SwipeRecyclerView
android:id="@+id/rvMaterialList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="20dp"
android:overScrollMode="never"
android:scrollbars="vertical"
tools:itemCount="3"
tools:listitem="@layout/list_item_food_material" />
</LinearLayout>
<TextView
android:id="@+id/btnSubmit"
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_margin="30dp"
android:background="@drawable/shape_green_bg"
android:foreground="?android:attr/selectableItemBackground"
android:gravity="center"
android:text="提交"
android:textColor="@color/white"
android:textSize="32sp"
android:textStyle="bold" />
</LinearLayout>