style(ui): 优化食物识别界面布局和对话框标题

- 将食物照片区域包装在 CardView 中并调整样式
- 为食材列表容器添加滚动指示条功能
- 替换 RecyclerView 的滚动条为自定义指示条
- 更新多个对话框的标题文本以提高一致性
- 在准备烹饪界面添加重量显示文本
- 优化适配器数据更新和列表滚动逻辑
- 防止 StateFlow 重放导致的重复数据加载
- 添加新的滚动条绘制资源文件
This commit is contained in:
2026-04-27 13:50:56 +08:00
parent b110cc97a0
commit 634f23a76a
12 changed files with 205 additions and 78 deletions
@@ -65,7 +65,7 @@ class CollectedFoodActivity : BaseActivity() {
appendText(" 删除后无法恢复,${separator}确认要删除吗?")
}
CommonDialog(this)
.setTitle("温馨提示")
.setTitle("删除提示")
.setContent(content)
.setNegativeButton("取消")
.setPositiveButton("确认") {
@@ -117,8 +117,8 @@ class DishSamplingActivity : BaseActivity() {
private fun saveDataRemindDialog() {
CommonDialog(this)
.setTitle("温馨提示")
.setContent("您好,当前页面存在未保存的数据,确认返回吗?")
.setTitle("返回提示")
.setContent("您好,当前页面存在未保存的数据,\n确认返回吗?")
.setNegativeButton("取消")
.setPositiveButton("确认") {
if (pageFrom == HOME) {
@@ -6,6 +6,7 @@ import android.os.Bundle
import androidx.activity.addCallback
import androidx.core.content.IntentCompat
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.shuwei.dish.match.adapter.GoodsInfoSearchAdapter
import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.databinding.ActivityFoodRecognizeBinding
@@ -93,6 +94,15 @@ class FoodRecognizeActivity : BaseActivity() {
/** 当前待延迟更新的目标重量,避免相同重量重复重置计时器 */
private var pendingWeight = 0.0
/** 右侧自定义滚动指示条最小高度 */
private val scrollIndicatorMinHeightPx by lazy { (24 * resources.displayMetrics.density).toInt() }
private val scrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
updateScrollIndicator()
}
}
private val adapter = GoodsInfoSearchAdapter(list).apply {
setOnItemClickListener { _, _, position ->
list.forEachIndexed { i, item -> item.isClicked = (i == position) }
@@ -166,9 +176,13 @@ class FoodRecognizeActivity : BaseActivity() {
* 初始化 RecyclerView
*/
private fun initRecyclerView() {
binding.recyclerView.run {
layoutManager = GridLayoutManager(this@FoodRecognizeActivity, 2)
adapter = this@FoodRecognizeActivity.adapter
binding.recyclerView.let {
it.layoutManager = GridLayoutManager(this@FoodRecognizeActivity, 2)
it.adapter = adapter
it.isVerticalScrollBarEnabled = false
it.removeOnScrollListener(scrollListener)
it.addOnScrollListener(scrollListener)
it.post { updateScrollIndicator() }
}
updateListVisibility()
}
@@ -178,12 +192,57 @@ class FoodRecognizeActivity : BaseActivity() {
*/
private fun updateListVisibility() {
if (list.isEmpty()) {
binding.recyclerView.gone()
binding.flListContainer.gone()
} else {
binding.recyclerView.visible()
binding.flListContainer.visible()
binding.recyclerView.post { updateScrollIndicator() }
}
}
/**
* 根据 RecyclerView 滚动状态刷新右侧自定义指示条
*/
private fun updateScrollIndicator() {
val recyclerView = binding.recyclerView
val track = binding.vScrollIndicatorTrack
val thumb = binding.vScrollIndicatorThumb
if (!recyclerView.canScrollVertically(1) && !recyclerView.canScrollVertically(-1)) {
track.gone()
thumb.gone()
return
}
val range = recyclerView.computeVerticalScrollRange()
val extent = recyclerView.computeVerticalScrollExtent()
val offset = recyclerView.computeVerticalScrollOffset()
if (range <= 0 || extent <= 0 || range <= extent) {
track.gone()
thumb.gone()
return
}
val trackHeight = track.height.takeIf { it > 0 } ?: recyclerView.height
if (trackHeight <= 0) return
val thumbHeight = ((extent.toFloat() / range) * trackHeight)
.toInt()
.coerceAtLeast(scrollIndicatorMinHeightPx)
.coerceAtMost(trackHeight)
val maxTop = (trackHeight - thumbHeight).coerceAtLeast(0)
val scrollable = (range - extent).coerceAtLeast(1)
val thumbTop = ((offset.toFloat() / scrollable) * maxTop).toInt().coerceIn(0, maxTop)
if (thumb.layoutParams.height != thumbHeight) {
thumb.layoutParams = thumb.layoutParams.apply { height = thumbHeight }
}
thumb.translationY = thumbTop.toFloat()
track.visible()
thumb.visible()
}
/**
* 初始化按钮点击事件
*/
@@ -219,6 +278,7 @@ class FoodRecognizeActivity : BaseActivity() {
adapter.notifyDataSetChanged()
binding.tvSelectedFood.text = item.goodsName ?: "-"
updateListVisibility()
binding.recyclerView.post { updateScrollIndicator() }
}
/**
@@ -122,11 +122,18 @@ class PrepareCookActivity : BaseActivity() {
}
private fun addViewClickListener() {
binding.tvShowWeight.clickWithDebounce { WeightUtil.tareTwo(AddressUtil.ONE) }
WeightUtil.addWeightListener(
weightKey = TAG,
getWeight = { address, state, weight ->
if (address == AddressUtil.ONE && state == WeightUtil.STATE_STABLE) {
runOnUiThread {
binding.tvShowWeight.run {
if (tag != weight) {
text = "称重:${weight}g"
tag = weight
}
}
recognizeFood(weight)
}
}
@@ -222,13 +229,12 @@ class PrepareCookActivity : BaseActivity() {
existing.isSetFinished = true
dishPartAdapter.notifyItemChanged(list.indexOf(existing))
} else {
list.add(entity.also {
dishPartAdapter.add(entity.also {
it.isNewDishType = true
it.isSetFinished = true
})
val position = list.size - 1
dishPartAdapter.notifyItemInserted(position)
binding.rvDishPartList.smoothScrollToPosition(position)
binding.rvDishPartList.smoothScrollToPosition(list.size - 1)
}
}
@@ -280,11 +286,14 @@ class PrepareCookActivity : BaseActivity() {
netViewModel.foodDetailState.collect { state ->
when (state) {
is UiState.Success -> {
// 防止 StateFlow 重放导致 list 被重复清空
if (isDetailLoaded) return@collect
val detail = state.data
if (detail == null) {
toast("查询菜品信息为空")
return@collect
}
isDetailLoaded = true
loadDishDetail(detail)
}
@@ -336,6 +345,7 @@ class PrepareCookActivity : BaseActivity() {
}
private var firstReqSize = 0
private var isDetailLoaded = false
private val list = mutableListOf<CookFoodGoodsEntity>()
private val dishPartAdapter by lazy {
DishPartAdapter(list = list).apply {
@@ -438,8 +448,8 @@ class PrepareCookActivity : BaseActivity() {
*/
private fun saveDataRemindDialog() {
CommonDialog(this)
.setTitle("温馨提示")
.setContent("您好,当前页面存在未保存的数据,确认返回吗?")
.setTitle("返回提示")
.setContent("您好,当前页面存在未保存的数据,\n确认返回吗?")
.setNegativeButton("取消")
.setPositiveButton("确认") {
if (SpTool.cookMode == 1 && pageFrom == HOME) {
@@ -369,7 +369,7 @@ class SamplingListActivity : BaseActivity() {
private fun showDeviceConfigDialog() {
CommonDialog(this)
.setTitle("温馨提示")
.setTitle("配置提示")
.setContent("请先在设备配置页面进行调料设置")
.setNegativeButton("取消")
.setPositiveButton("去设置") { startActivity<SettingActivity>() }
@@ -84,7 +84,7 @@ class SelectDishActivity : BaseActivity() {
private fun showDeviceConfigDialog() {
CommonDialog(this)
.setTitle("温馨提示")
.setTitle("配置提示")
.setContent("请先在设备配置页面进行调料设置")
.setNegativeButton("取消")
.setPositiveButton("去设置") { startActivity<SettingActivity>() }
@@ -115,7 +115,7 @@ class SettingActivity : BaseActivity() {
private fun showNotGrantedCameraPermissionDialog() {
CommonDialog(this)
.setTitle("温馨提示")
.setTitle("权限提示")
.setContent("暂无相机权限,无法使用食材采集功能,请确认是否同意开始权限?")
.setNegativeButton("不同意")
.setPositiveButton("同意") {
@@ -423,8 +423,8 @@ class SubmitFoodActivity : BaseActivity() {
*/
private fun remindSaveDataDialog() {
CommonDialog(this)
.setTitle("温馨提示")
.setContent("请确认是否存在未保存的数据?")
.setTitle("保存提示")
.setContent("可能存在未保存的数据,请确认")
.setNegativeButton("取消,留在页面")
.setNeutralButton("返回上页,不保存") { finish() }
.setPositiveButton("保存为烹饪中菜品") { cook() }
@@ -436,7 +436,7 @@ class SubmitFoodActivity : BaseActivity() {
*/
private fun showRemindDialog(content: CharSequence, action: () -> Unit) {
CommonDialog(this)
.setTitle("温馨提示")
.setTitle(food?.foodName ?: "温馨提示")
.setContent(content)
.setNegativeButton("取消")
.setPositiveButton("确认") { action() }
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:width="6dp" />
<!-- <solid android:color="#66C4C4C4" />-->
<solid android:color="@color/dish_green" />
<corners android:radius="3dp" />
</shape>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:width="10dp" />
<solid android:color="#33000000" />
<corners android:radius="5dp" />
</shape>
@@ -8,83 +8,115 @@
android:padding="16dp"
tools:background="@color/white">
<!-- 食材照片,权重占满剩余空间 -->
<!-- 食材照片,权重占满剩余空间 -->
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:cardBackgroundColor="@color/white"
app:cardElevation="0dp"
app:cardCornerRadius="8dp">
<ImageView
android:id="@+id/ivFoodPhoto"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:scaleType="centerCrop"
tools:ignore="ContentDescription"
tools:src="@color/bg_color" />
tools:src="@mipmap/ic_launcher" />
</androidx.cardview.widget.CardView>
<!-- 识别到的食材列表,无数据时隐藏 -->
<FrameLayout
android:id="@+id/flListContainer"
android:layout_width="match_parent"
android:layout_height="260dp"
android:layout_marginTop="12dp">
<!-- 识别到的食材列表,无数据时隐藏 -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:maxHeight="200dp"
android:nestedScrollingEnabled="false"
android:layout_height="match_parent"
android:overScrollMode="never"
android:visibility="gone"
android:paddingEnd="6dp"
android:paddingStart="0dp"
android:clipToPadding="false"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
tools:itemCount="4"
tools:listitem="@layout/list_item_search_goods_info"
tools:visibility="visible" />
<!-- 选中的食材名称 -->
<TextView
android:id="@+id/tvSelectedFood"
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_marginTop="12dp"
android:gravity="center"
android:text="-"
android:textColor="@color/black333"
android:textSize="32sp" />
<View
android:id="@+id/vScrollIndicatorTrack"
android:layout_width="4dp"
android:layout_height="match_parent"
android:layout_gravity="end|top"
android:layout_marginEnd="2dp"
android:background="@drawable/scrollbar_track_food_recognize"
android:visibility="gone" />
<!-- 秤1重量 -->
<TextView
android:id="@+id/tvWeight"
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_marginTop="8dp"
android:background="@drawable/shape_white_12_corners"
android:gravity="center"
android:text="0.0 g"
android:textColor="@color/black333"
android:textSize="32sp" />
<View
android:id="@+id/vScrollIndicatorThumb"
android:layout_width="6dp"
android:layout_height="24dp"
android:layout_gravity="end|top"
android:layout_marginEnd="1dp"
android:background="@drawable/scrollbar_thumb_food_recognize"
android:visibility="gone" />
</FrameLayout>
<!-- 更换食材 -->
<TextView
android:id="@+id/btnChangeFood"
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_marginTop="30dp"
android:gravity="center"
android:background="@drawable/shape_green_bg"
android:foreground="?android:attr/selectableItemBackground"
android:text="更换食材"
android:textColor="@color/white"
android:textSize="32sp"
android:textStyle="bold" />
<!-- 选中的食材名称 -->
<TextView
android:id="@+id/tvSelectedFood"
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_marginTop="12dp"
android:gravity="center"
android:text="-"
android:textColor="@color/black333"
android:textSize="32sp" />
<!-- 取消 -->
<TextView
android:id="@+id/btnCancel"
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_marginTop="30dp"
android:layout_marginBottom="30dp"
android:gravity="center"
android:background="@drawable/shape_white_12_corners"
android:foreground="?android:attr/selectableItemBackground"
android:text="取消"
android:textColor="@color/black333"
android:textSize="32sp"
android:textStyle="bold" />
<!-- 秤1重量 -->
<TextView
android:id="@+id/tvWeight"
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_marginTop="8dp"
android:background="@drawable/shape_white_12_corners"
android:gravity="center"
android:text="0.0 g"
android:textColor="@color/black333"
android:textSize="32sp" />
<!-- 更换食材 -->
<TextView
android:id="@+id/btnChangeFood"
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_marginTop="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" />
<!-- 取消 -->
<TextView
android:id="@+id/btnCancel"
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_marginTop="30dp"
android:layout_marginBottom="30dp"
android:background="@drawable/shape_white_12_corners"
android:foreground="?android:attr/selectableItemBackground"
android:gravity="center"
android:text="取消"
android:textColor="@color/black333"
android:textSize="32sp"
android:textStyle="bold" />
</LinearLayout>
@@ -75,6 +75,16 @@
android:textColor="@color/black666"
android:textSize="28sp" />
<TextView
android:id="@+id/tvShowWeight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:padding="10dp"
tools:text="0g"
android:textColor="@color/black666"
android:textSize="28sp" />
<FrameLayout
android:id="@+id/flCameraContainer"
android:layout_width="1dp"