feat(dialog): 调料选择对话框增加滑动冲突处理和重量传递功能

- 修改 dialog_seasoning_select.xml 布局高度为 match_parent 并添加标题文本
- 调整 SmartRefreshLayout 高度限制并移除 RecyclerView 内边距
- 在 FoodRecognizeActivity 中新增 currentWeight 参数传递机制
- 移除 WeightUtil 监听器相关代码并初始化当前重量值
- 在 PrepareCookActivity 中传递当前重量到识别页面
- 从 SeasoningConfigFragment 和 SeasoningSelectDialog 中移除无用参数
- 添加 Window.Callback 触摸事件拦截解决 RecyclerView 滑动冲突
- 实现触摸点位置检测动态控制 BottomSheet 拖拽行为
This commit is contained in:
2026-04-24 10:07:11 +08:00
parent f3d57177b8
commit 617ccfb456
5 changed files with 77 additions and 48 deletions
@@ -1,17 +1,18 @@
package com.shuwei.dish.match.dialog
import android.annotation.SuppressLint
import android.graphics.Rect
import android.os.Handler
import android.os.Looper
import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
import androidx.core.view.isEmpty
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.SeasoningSearchAdapter
@@ -30,7 +31,6 @@ import com.shuwei.dish.match.utils.ext.toast
*/
class SeasoningSelectDialog(
private val activity: BaseActivity,
private val clickIndex: Int,
private val onItemSelected: (item: SeasoningEntity) -> Unit
) : BottomSheetDialog(activity, R.style.BottomSheet) {
@@ -84,18 +84,10 @@ class SeasoningSelectDialog(
addOnActionSearchListener { searchGoods(this) }
}
// RecyclerView 初始化及滑动冲突处理
// RecyclerView 初始化
binding.recyclerView.run {
layoutManager = GridLayoutManager(activity, 3, GridLayoutManager.VERTICAL, false)
adapter = this@SeasoningSelectDialog.adapter
addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(rv: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(rv, dx, dy)
// 解决 RecyclerView 与 SmartRefreshLayout 滑动冲突
val topRowVerticalPosition = if (rv.isEmpty()) 0 else rv.getChildAt(0).top
binding.refreshLayout.setNestedScrollingEnabled(topRowVerticalPosition >= 0)
}
})
}
binding.refreshLayout.run {
@@ -109,11 +101,32 @@ class SeasoningSelectDialog(
binding.root.setOnClickListener { KeyboardUtil.hideKeyboard(it) }
}
/** 弹窗显示时自动加载默认调料列表 */
/** 弹窗显示时自动加载默认调料列表,并在 Window.Callback 层提前拦截触摸以解决滑动冲突 */
override fun show() {
super.show()
pageNo = 1
getGoodsList()
// Window.Callback.dispatchTouchEvent 在整个 View 树的 onInterceptTouchEvent 之前执行,
// 是最早能感知触摸的时机。在此处判断触摸点位置来控制 behavior.isDraggable
// 从而确保 BottomSheetBehavior 在 CoordinatorLayout.onInterceptTouchEvent 时
// 已经是禁用状态,不会拦截列表区域的滑动手势。
val originalCallback = window?.callback ?: return
window?.callback = object : Window.Callback by originalCallback {
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
val rect = Rect()
binding.refreshLayout.getGlobalVisibleRect(rect)
// 触摸点落在列表区域内则禁止 BottomSheet 拖拽
behavior.isDraggable = !rect.contains(event.rawX.toInt(), event.rawY.toInt())
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
behavior.isDraggable = true
}
}
return originalCallback.dispatchTouchEvent(event)
}
}
}
/**
@@ -12,7 +12,6 @@ import com.shuwei.dish.match.databinding.ActivityFoodRecognizeBinding
import com.shuwei.dish.match.dialog.FoodSearchDialog
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.utils.ActivityManager
import com.shuwei.dish.match.utils.AddressUtil
import com.shuwei.dish.match.utils.WeightUtil
import com.shuwei.dish.match.utils.ext.clickWithDebounce
import com.shuwei.dish.match.utils.ext.gone
@@ -28,6 +27,7 @@ class FoodRecognizeActivity : BaseActivity() {
companion object {
private const val TAG = "FoodRecognizeActivity"
private const val EXTRA_IMAGE_URI = "extra_image_uri"
private const val EXTRA_CURRENT_WEIGHT = "extra_current_weight"
const val EXTRA_SELECTED_ITEM = "extra_selected_item"
const val IS_MANUAL_CANCEL = "isManualCancel"
@@ -44,12 +44,14 @@ class FoodRecognizeActivity : BaseActivity() {
fun start(
activity: BaseActivity,
imageUri: String? = null,
currentWeight: Double = 0.0,
goodsList: ArrayList<CookFoodGoodsEntity> = arrayListOf(),
finishCallback: (Boolean, CookFoodGoodsEntity?) -> Unit = { _, _ -> }
) {
pendingGoodsList = goodsList
val launchIntent = Intent(activity, FoodRecognizeActivity::class.java).apply {
putExtra(EXTRA_IMAGE_URI, imageUri)
putExtra(EXTRA_CURRENT_WEIGHT, currentWeight)
}
activity.startActivity(launchIntent) { resultIntent ->
val isManualCancel = resultIntent?.getBooleanExtra(IS_MANUAL_CANCEL, false) ?: false
@@ -145,6 +147,8 @@ class FoodRecognizeActivity : BaseActivity() {
if (!imageUri.isNullOrBlank()) {
binding.ivFoodPhoto.setImageURI(imageUri.toUri())
}
currentWeight = intent.getDoubleExtra(EXTRA_CURRENT_WEIGHT, 0.0)
binding.tvWeight.text = "${currentWeight}g"
// 取出内存缓存的食材列表,取用后立即清空
val goodsList = pendingGoodsList?.also { pendingGoodsList = null }
@@ -153,6 +157,7 @@ class FoodRecognizeActivity : BaseActivity() {
// 默认选中第一项
selectedPosition = 0
list[0].isClicked = true
list[0].useWeight = currentWeight
binding.tvSelectedFood.text = list[0].goodsName ?: "-"
}
}
@@ -222,40 +227,40 @@ class FoodRecognizeActivity : BaseActivity() {
* 低于阈值说明食材已被取走,保留上次有效值等待页面关闭
*/
private fun registerWeightListener() {
WeightUtil.addWeightListener(TAG) { address, state, weight ->
if (address == AddressUtil.ONE && state == WeightUtil.STATE_STABLE) {
runOnUiThread {
currentWeight = weight
binding.tvWeight.run {
if (tag != weight) {
text = "$weight g"
tag = weight
}
}
if (weight >= PrepareCookActivity.WEIGHT_RECOGNIZE_VALUE) {
adapter.items.firstOrNull { it.isClicked }?.let { item ->
if (pendingWeight != weight) {
// 无论增减,均延迟更新,避免手拿物品时的瞬时增重干扰
cancelPendingWeightUpdate()
pendingWeight = weight
pendingWeightRunnable = Runnable {
item.useWeight = weight
pendingWeightRunnable = null
}.also { handler.postDelayed(it, 3000) }
}
}
}
}
}
}
// WeightUtil.addWeightListener(TAG) { address, state, weight ->
// if (address == AddressUtil.ONE && state == WeightUtil.STATE_STABLE) {
// runOnUiThread {
// currentWeight = weight
// binding.tvWeight.run {
// if (tag != weight) {
// text = "$weight g"
// tag = weight
// }
// }
// if (weight >= PrepareCookActivity.WEIGHT_RECOGNIZE_VALUE) {
// adapter.items.firstOrNull { it.isClicked }?.let { item ->
// if (pendingWeight != weight) {
// // 无论增减,均延迟更新,避免手拿物品时的瞬时增重干扰
// cancelPendingWeightUpdate()
// pendingWeight = weight
// pendingWeightRunnable = Runnable {
// item.useWeight = weight
// pendingWeightRunnable = null
// }.also { handler.postDelayed(it, 3000) }
// }
// }
// }
// }
// }
// }
}
/**
* 取消待执行的延迟重量更新任务
*/
private fun cancelPendingWeightUpdate() {
pendingWeightRunnable?.let { handler.removeCallbacks(it) }
pendingWeightRunnable = null
// pendingWeightRunnable?.let { handler.removeCallbacks(it) }
// pendingWeightRunnable = null
}
/**
@@ -196,6 +196,7 @@ class PrepareCookActivity : BaseActivity() {
if (weight < WEIGHT_RECOGNIZE_VALUE) {
showRecognizePage = false
manualCancelFlag = false
lastWeight = weight
FoodRecognizeActivity.close()
return
}
@@ -515,9 +516,11 @@ class PrepareCookActivity : BaseActivity() {
}
)
}
lastWeight = currentWeight
FoodRecognizeActivity.start(
activity = this,
imageUri = lastPhotoUri?.toString(),
currentWeight = currentWeight,
goodsList = ArrayList(foodList),
finishCallback = { isManualCancel, entity ->
Log.d(TAG, "queryFood: isManualCancel=$isManualCancel, entity=${entity.toJsonString()}")
@@ -136,7 +136,6 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
) {
SeasoningSelectDialog(
activity = currentActivity,
clickIndex = position,
onItemSelected = callback
).show()
}
@@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:gravity="center_horizontal"
tools:background="@color/white"
android:orientation="vertical">
@@ -15,6 +15,16 @@
android:layout_marginTop="30dp"
android:background="@drawable/shape_gray_dc_5" />
<TextView
android:id="@+id/tvSheetName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:textColor="@color/black333"
android:textSize="36sp"
android:textStyle="bold"
android:text="调料检索" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="100dp"
@@ -57,8 +67,10 @@
<com.scwang.smart.refresh.layout.SmartRefreshLayout
android:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="700dp"
android:layout_marginTop="10dp"
android:layout_marginStart="45dp"
android:layout_marginEnd="45dp"
app:srlEnableOverScrollDrag="false">
<com.scwang.smart.refresh.header.ClassicsHeader
@@ -69,11 +81,8 @@
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="45dp"
android:layout_marginEnd="45dp"
android:layout_marginTop="15dp"
android:layout_marginBottom="15dp"
android:minHeight="380dp"
android:nestedScrollingEnabled="true"
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"