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 package com.shuwei.dish.match.dialog
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.Rect
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.view.Gravity import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager import android.view.WindowManager
import androidx.core.view.isEmpty
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
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.SeasoningSearchAdapter import com.shuwei.dish.match.adapter.SeasoningSearchAdapter
@@ -30,7 +31,6 @@ import com.shuwei.dish.match.utils.ext.toast
*/ */
class SeasoningSelectDialog( class SeasoningSelectDialog(
private val activity: BaseActivity, private val activity: BaseActivity,
private val clickIndex: Int,
private val onItemSelected: (item: SeasoningEntity) -> Unit private val onItemSelected: (item: SeasoningEntity) -> Unit
) : BottomSheetDialog(activity, R.style.BottomSheet) { ) : BottomSheetDialog(activity, R.style.BottomSheet) {
@@ -84,18 +84,10 @@ class SeasoningSelectDialog(
addOnActionSearchListener { searchGoods(this) } addOnActionSearchListener { searchGoods(this) }
} }
// RecyclerView 初始化及滑动冲突处理 // RecyclerView 初始化
binding.recyclerView.run { binding.recyclerView.run {
layoutManager = GridLayoutManager(activity, 3, GridLayoutManager.VERTICAL, false) layoutManager = GridLayoutManager(activity, 3, GridLayoutManager.VERTICAL, false)
adapter = this@SeasoningSelectDialog.adapter 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 { binding.refreshLayout.run {
@@ -109,11 +101,32 @@ class SeasoningSelectDialog(
binding.root.setOnClickListener { KeyboardUtil.hideKeyboard(it) } binding.root.setOnClickListener { KeyboardUtil.hideKeyboard(it) }
} }
/** 弹窗显示时自动加载默认调料列表 */ /** 弹窗显示时自动加载默认调料列表,并在 Window.Callback 层提前拦截触摸以解决滑动冲突 */
override fun show() { override fun show() {
super.show() super.show()
pageNo = 1 pageNo = 1
getGoodsList() 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.dialog.FoodSearchDialog
import com.shuwei.dish.match.entity.CookFoodGoodsEntity import com.shuwei.dish.match.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.utils.ActivityManager 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.WeightUtil
import com.shuwei.dish.match.utils.ext.clickWithDebounce import com.shuwei.dish.match.utils.ext.clickWithDebounce
import com.shuwei.dish.match.utils.ext.gone import com.shuwei.dish.match.utils.ext.gone
@@ -28,6 +27,7 @@ class FoodRecognizeActivity : BaseActivity() {
companion object { companion object {
private const val TAG = "FoodRecognizeActivity" private const val TAG = "FoodRecognizeActivity"
private const val EXTRA_IMAGE_URI = "extra_image_uri" 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 EXTRA_SELECTED_ITEM = "extra_selected_item"
const val IS_MANUAL_CANCEL = "isManualCancel" const val IS_MANUAL_CANCEL = "isManualCancel"
@@ -44,12 +44,14 @@ class FoodRecognizeActivity : BaseActivity() {
fun start( fun start(
activity: BaseActivity, activity: BaseActivity,
imageUri: String? = null, imageUri: String? = null,
currentWeight: Double = 0.0,
goodsList: ArrayList<CookFoodGoodsEntity> = arrayListOf(), goodsList: ArrayList<CookFoodGoodsEntity> = arrayListOf(),
finishCallback: (Boolean, CookFoodGoodsEntity?) -> Unit = { _, _ -> } finishCallback: (Boolean, CookFoodGoodsEntity?) -> Unit = { _, _ -> }
) { ) {
pendingGoodsList = goodsList pendingGoodsList = goodsList
val launchIntent = Intent(activity, FoodRecognizeActivity::class.java).apply { val launchIntent = Intent(activity, FoodRecognizeActivity::class.java).apply {
putExtra(EXTRA_IMAGE_URI, imageUri) putExtra(EXTRA_IMAGE_URI, imageUri)
putExtra(EXTRA_CURRENT_WEIGHT, currentWeight)
} }
activity.startActivity(launchIntent) { resultIntent -> activity.startActivity(launchIntent) { resultIntent ->
val isManualCancel = resultIntent?.getBooleanExtra(IS_MANUAL_CANCEL, false) ?: false val isManualCancel = resultIntent?.getBooleanExtra(IS_MANUAL_CANCEL, false) ?: false
@@ -145,6 +147,8 @@ class FoodRecognizeActivity : BaseActivity() {
if (!imageUri.isNullOrBlank()) { if (!imageUri.isNullOrBlank()) {
binding.ivFoodPhoto.setImageURI(imageUri.toUri()) binding.ivFoodPhoto.setImageURI(imageUri.toUri())
} }
currentWeight = intent.getDoubleExtra(EXTRA_CURRENT_WEIGHT, 0.0)
binding.tvWeight.text = "${currentWeight}g"
// 取出内存缓存的食材列表,取用后立即清空 // 取出内存缓存的食材列表,取用后立即清空
val goodsList = pendingGoodsList?.also { pendingGoodsList = null } val goodsList = pendingGoodsList?.also { pendingGoodsList = null }
@@ -153,6 +157,7 @@ class FoodRecognizeActivity : BaseActivity() {
// 默认选中第一项 // 默认选中第一项
selectedPosition = 0 selectedPosition = 0
list[0].isClicked = true list[0].isClicked = true
list[0].useWeight = currentWeight
binding.tvSelectedFood.text = list[0].goodsName ?: "-" binding.tvSelectedFood.text = list[0].goodsName ?: "-"
} }
} }
@@ -222,40 +227,40 @@ class FoodRecognizeActivity : BaseActivity() {
* 低于阈值说明食材已被取走,保留上次有效值等待页面关闭 * 低于阈值说明食材已被取走,保留上次有效值等待页面关闭
*/ */
private fun registerWeightListener() { private fun registerWeightListener() {
WeightUtil.addWeightListener(TAG) { address, state, weight -> // WeightUtil.addWeightListener(TAG) { address, state, weight ->
if (address == AddressUtil.ONE && state == WeightUtil.STATE_STABLE) { // if (address == AddressUtil.ONE && state == WeightUtil.STATE_STABLE) {
runOnUiThread { // runOnUiThread {
currentWeight = weight // currentWeight = weight
binding.tvWeight.run { // binding.tvWeight.run {
if (tag != weight) { // if (tag != weight) {
text = "$weight g" // text = "$weight g"
tag = weight // tag = weight
} // }
} // }
if (weight >= PrepareCookActivity.WEIGHT_RECOGNIZE_VALUE) { // if (weight >= PrepareCookActivity.WEIGHT_RECOGNIZE_VALUE) {
adapter.items.firstOrNull { it.isClicked }?.let { item -> // adapter.items.firstOrNull { it.isClicked }?.let { item ->
if (pendingWeight != weight) { // if (pendingWeight != weight) {
// 无论增减,均延迟更新,避免手拿物品时的瞬时增重干扰 // // 无论增减,均延迟更新,避免手拿物品时的瞬时增重干扰
cancelPendingWeightUpdate() // cancelPendingWeightUpdate()
pendingWeight = weight // pendingWeight = weight
pendingWeightRunnable = Runnable { // pendingWeightRunnable = Runnable {
item.useWeight = weight // item.useWeight = weight
pendingWeightRunnable = null // pendingWeightRunnable = null
}.also { handler.postDelayed(it, 3000) } // }.also { handler.postDelayed(it, 3000) }
} // }
} // }
} // }
} // }
} // }
} // }
} }
/** /**
* 取消待执行的延迟重量更新任务 * 取消待执行的延迟重量更新任务
*/ */
private fun cancelPendingWeightUpdate() { private fun cancelPendingWeightUpdate() {
pendingWeightRunnable?.let { handler.removeCallbacks(it) } // pendingWeightRunnable?.let { handler.removeCallbacks(it) }
pendingWeightRunnable = null // pendingWeightRunnable = null
} }
/** /**
@@ -196,6 +196,7 @@ class PrepareCookActivity : BaseActivity() {
if (weight < WEIGHT_RECOGNIZE_VALUE) { if (weight < WEIGHT_RECOGNIZE_VALUE) {
showRecognizePage = false showRecognizePage = false
manualCancelFlag = false manualCancelFlag = false
lastWeight = weight
FoodRecognizeActivity.close() FoodRecognizeActivity.close()
return return
} }
@@ -515,9 +516,11 @@ class PrepareCookActivity : BaseActivity() {
} }
) )
} }
lastWeight = currentWeight
FoodRecognizeActivity.start( FoodRecognizeActivity.start(
activity = this, activity = this,
imageUri = lastPhotoUri?.toString(), imageUri = lastPhotoUri?.toString(),
currentWeight = currentWeight,
goodsList = ArrayList(foodList), goodsList = ArrayList(foodList),
finishCallback = { isManualCancel, entity -> finishCallback = { isManualCancel, entity ->
Log.d(TAG, "queryFood: isManualCancel=$isManualCancel, entity=${entity.toJsonString()}") Log.d(TAG, "queryFood: isManualCancel=$isManualCancel, entity=${entity.toJsonString()}")
@@ -136,7 +136,6 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
) { ) {
SeasoningSelectDialog( SeasoningSelectDialog(
activity = currentActivity, activity = currentActivity,
clickIndex = position,
onItemSelected = callback onItemSelected = callback
).show() ).show()
} }
@@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto" 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="match_parent"
android:gravity="center_horizontal" android:gravity="center_horizontal"
tools:background="@color/white" tools:background="@color/white"
android:orientation="vertical"> android:orientation="vertical">
@@ -15,6 +15,16 @@
android:layout_marginTop="30dp" android:layout_marginTop="30dp"
android:background="@drawable/shape_gray_dc_5" /> 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 <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="100dp" android:layout_height="100dp"
@@ -57,8 +67,10 @@
<com.scwang.smart.refresh.layout.SmartRefreshLayout <com.scwang.smart.refresh.layout.SmartRefreshLayout
android:id="@+id/refreshLayout" android:id="@+id/refreshLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="700dp"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_marginStart="45dp"
android:layout_marginEnd="45dp"
app:srlEnableOverScrollDrag="false"> app:srlEnableOverScrollDrag="false">
<com.scwang.smart.refresh.header.ClassicsHeader <com.scwang.smart.refresh.header.ClassicsHeader
@@ -69,11 +81,8 @@
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginStart="45dp"
android:layout_marginEnd="45dp"
android:layout_marginTop="15dp" android:layout_marginTop="15dp"
android:layout_marginBottom="15dp" android:layout_marginBottom="15dp"
android:minHeight="380dp"
android:nestedScrollingEnabled="true" android:nestedScrollingEnabled="true"
android:overScrollMode="never" android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"