feat(dialog): FoodSearchDialog 新增空页面效果及 Window.Callback 手势冲突修复
- FoodSearchDialog:adapter 启用 isStateViewEnable,新增 loadEmptyView() 通过 adapter.stateView 展示空视图,替换原 toast 提示;覆写 show() 通过 Window.Callback.dispatchTouchEvent 动态控制 behavior.isDraggable,解决列表滑动与 BottomSheet 拖拽的手势冲突;注释旧 addOnScrollListener 方案保留备查 - SeasoningSelectDialog:同步补齐空页面效果,修复网络错误时也触发 loadEmptyView,优化 Handler 为 View.postDelayed - FoodRecognizeActivity:修复识别结果回填后列表未显示的问题 - 新增 scale包架构分析.md 文档
This commit is contained in:
@@ -1,21 +1,29 @@
|
||||
package com.shuwei.dish.match.dialog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Rect
|
||||
import android.text.Editable
|
||||
import android.view.KeyboardShortcutGroup
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.isEmpty
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.shuwei.dish.match.R
|
||||
import com.shuwei.dish.match.adapter.FoodAdapter
|
||||
import com.shuwei.dish.match.base.BaseActivity
|
||||
import com.shuwei.dish.match.databinding.DialogFoodSearchBinding
|
||||
import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding
|
||||
import com.shuwei.dish.match.dialog.SeasoningSelectDialog.Companion.PAGE_SIZE
|
||||
import com.shuwei.dish.match.entity.GoodsItem
|
||||
import com.shuwei.dish.match.net.UiState
|
||||
@@ -42,20 +50,29 @@ class FoodSearchDialog(
|
||||
|
||||
private val binding = DialogFoodSearchBinding.inflate(LayoutInflater.from(activity))
|
||||
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 = "洋葱丁"))
|
||||
// }
|
||||
.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 {
|
||||
isStateViewEnable = true
|
||||
setOnItemClickListener { _, _, position ->
|
||||
list[position].isClicked = true
|
||||
notifyItemChanged(position)
|
||||
@@ -92,18 +109,18 @@ class FoodSearchDialog(
|
||||
addOnActionSearchListener { searchGoods(this) }
|
||||
}
|
||||
|
||||
// RecyclerView 初始化及滑动冲突处理
|
||||
// RecyclerView 初始化
|
||||
binding.recyclerView.run {
|
||||
layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.VERTICAL, false)
|
||||
adapter = this@FoodSearchDialog.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)
|
||||
}
|
||||
})
|
||||
// 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 {
|
||||
@@ -118,13 +135,51 @@ class FoodSearchDialog(
|
||||
|
||||
initObserver()
|
||||
|
||||
// 若有默认食材名称,自动填充并触发搜索
|
||||
//if (!defGoodsName.isNullOrBlank()) {
|
||||
binding.etSheetInput.setText(defGoodsName)
|
||||
pageNo = 1
|
||||
getGoodsList()
|
||||
KeyboardUtil.hideKeyboard(binding.root)
|
||||
//}
|
||||
// // 若有默认食材名称,自动填充并触发搜索
|
||||
// //if (!defGoodsName.isNullOrBlank()) {
|
||||
// binding.etSheetInput.setText(defGoodsName)
|
||||
// pageNo = 1
|
||||
// getGoodsList()
|
||||
// KeyboardUtil.hideKeyboard(binding.root)
|
||||
// //}
|
||||
}
|
||||
|
||||
/** 弹窗显示时在 Window.Callback 层提前拦截触摸,解决 BottomSheet 拖拽与列表滑动的手势冲突 */
|
||||
override fun show() {
|
||||
super.show()
|
||||
// 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)
|
||||
}
|
||||
|
||||
override fun onPointerCaptureChanged(hasCapture: Boolean) {
|
||||
originalCallback.onPointerCaptureChanged(hasCapture)
|
||||
}
|
||||
|
||||
override fun onProvideKeyboardShortcuts(
|
||||
data: List<KeyboardShortcutGroup?>?,
|
||||
menu: Menu?,
|
||||
deviceId: Int
|
||||
) {
|
||||
originalCallback.onProvideKeyboardShortcuts(data, menu, deviceId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,9 +234,12 @@ class FoodSearchDialog(
|
||||
private fun loadGoodsList(records: MutableList<GoodsItem>?) {
|
||||
finishRefresh()
|
||||
if (records.isNullOrEmpty()) {
|
||||
activity.toast("暂未搜索到食材信息")
|
||||
if (pageNo == 1) {
|
||||
loadEmptyView()
|
||||
}
|
||||
return
|
||||
}
|
||||
binding.recyclerView.layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.VERTICAL, false)
|
||||
if (pageNo == 1) list.clear()
|
||||
list.addAll(records)
|
||||
adapter.notifyDataSetChanged()
|
||||
@@ -194,4 +252,36 @@ class FoodSearchDialog(
|
||||
if (pageNo == 1) binding.refreshLayout.finishRefresh()
|
||||
else binding.refreshLayout.finishLoadMore()
|
||||
}
|
||||
|
||||
private var emptyViewBinding: LayoutEmptyViewBinding? = null
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun loadEmptyView() {
|
||||
try {
|
||||
binding.refreshLayout.setEnableRefresh(true)
|
||||
binding.refreshLayout.setEnableLoadMore(false)
|
||||
list.clear()
|
||||
adapter.notifyDataSetChanged()
|
||||
if (emptyViewBinding == null) {
|
||||
emptyViewBinding = LayoutEmptyViewBinding.inflate(
|
||||
LayoutInflater.from(activity),
|
||||
binding.recyclerView,
|
||||
false
|
||||
)
|
||||
}
|
||||
emptyViewBinding!!.tvContent.text = "暂无数据"
|
||||
emptyViewBinding!!.tvSubContent.text = "未查询到食材信息,请联系管理员添加"
|
||||
emptyViewBinding!!.root.layoutParams = FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
FrameLayout.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(activity)
|
||||
binding.recyclerView.post {
|
||||
emptyViewBinding!!.root.minimumHeight = binding.recyclerView.height
|
||||
adapter.stateView = emptyViewBinding!!.root
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,14 +13,17 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.Window
|
||||
import android.view.WindowManager
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.shuwei.dish.match.R
|
||||
import com.shuwei.dish.match.adapter.SeasoningSearchAdapter
|
||||
import com.shuwei.dish.match.base.BaseActivity
|
||||
import com.shuwei.dish.match.databinding.DialogSeasoningSelectBinding
|
||||
import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding
|
||||
import com.shuwei.dish.match.entity.GoodsItem
|
||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
||||
import com.shuwei.dish.match.net.UiState
|
||||
@@ -48,10 +51,11 @@ class SeasoningSelectDialog(
|
||||
private val binding = DialogSeasoningSelectBinding.inflate(LayoutInflater.from(activity))
|
||||
private val list = mutableListOf<SeasoningEntity>()
|
||||
private val adapter = SeasoningSearchAdapter(list).apply {
|
||||
setOnItemClickListener { _, _, position ->
|
||||
isStateViewEnable = true
|
||||
setOnItemClickListener { _, v, position ->
|
||||
list[position].isClicked = true
|
||||
notifyItemChanged(position)
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
v.postDelayed({
|
||||
onItemSelected(list[position])
|
||||
dismiss()
|
||||
}, 300)
|
||||
@@ -121,6 +125,9 @@ class SeasoningSelectDialog(
|
||||
is UiState.Error -> {
|
||||
activity.toast(state.msg)
|
||||
finishRefresh()
|
||||
if (pageNo == 1) {
|
||||
loadEmptyView()
|
||||
}
|
||||
}
|
||||
is UiState.Idle -> {}
|
||||
}
|
||||
@@ -195,9 +202,13 @@ class SeasoningSelectDialog(
|
||||
private fun loadGoodsList(records: MutableList<GoodsItem>?) {
|
||||
finishRefresh()
|
||||
if (records.isNullOrEmpty()) {
|
||||
activity.toast("暂未搜索到调料信息")
|
||||
//activity.toast("暂未搜索到调料信息")
|
||||
if (pageNo == 1) {
|
||||
loadEmptyView()
|
||||
}
|
||||
return
|
||||
}
|
||||
binding.recyclerView.layoutManager = GridLayoutManager(activity, 3, GridLayoutManager.VERTICAL, false)
|
||||
if (pageNo == 1) list.clear()
|
||||
list.addAll(records.map { it.toSeasoningEntity() })
|
||||
adapter.notifyDataSetChanged()
|
||||
@@ -210,4 +221,37 @@ class SeasoningSelectDialog(
|
||||
if (pageNo == 1) binding.refreshLayout.finishRefresh()
|
||||
else binding.refreshLayout.finishLoadMore()
|
||||
}
|
||||
|
||||
|
||||
private var emptyViewBinding: LayoutEmptyViewBinding? = null
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun loadEmptyView() {
|
||||
try {
|
||||
binding.refreshLayout.setEnableRefresh(true)
|
||||
binding.refreshLayout.setEnableLoadMore(false)
|
||||
list.clear()
|
||||
adapter.notifyDataSetChanged()
|
||||
if (emptyViewBinding == null) {
|
||||
emptyViewBinding = LayoutEmptyViewBinding.inflate(
|
||||
LayoutInflater.from(activity),
|
||||
binding.recyclerView,
|
||||
false
|
||||
)
|
||||
}
|
||||
emptyViewBinding!!.tvContent.text = "暂无数据"
|
||||
emptyViewBinding!!.tvSubContent.text = "未查询到调料信息,请联系管理员添加"
|
||||
emptyViewBinding!!.root.layoutParams = FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
FrameLayout.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(activity)
|
||||
binding.recyclerView.post {
|
||||
emptyViewBinding!!.root.minimumHeight = binding.recyclerView.height
|
||||
adapter.stateView = emptyViewBinding!!.root
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,6 +352,10 @@ class FoodRecognizeActivity : BaseActivity() {
|
||||
selectedPosition = 0
|
||||
}
|
||||
adapter.notifyDataSetChanged()
|
||||
if (list.isNotEmpty()) {
|
||||
binding.rvRecognizeList.visible()
|
||||
binding.tvNoData.gone()
|
||||
}
|
||||
updateRvParentLayoutHeight()
|
||||
binding.tvSelectedFood.text = item.goodsName ?: "-"
|
||||
updateListVisibility()
|
||||
|
||||
Reference in New Issue
Block a user