diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3b24cb5..095ded5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -111,6 +111,11 @@
android:theme="@style/Theme.DishMatch.NoSplash"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
+
(mu
b.root.layoutParams = (b.root.layoutParams as ViewGroup.MarginLayoutParams).also {
it.width = cellSize
it.height = cellSize
- it.setMargins(margin, margin, margin, margin)
+ // 第1行:顶部间距为0,底部间距2dp,左右保持原有 margin
+ if (viewType == 0) it.setMargins(margin, 0, margin, 2.dp)
+ else it.setMargins(margin, margin, margin, margin)
}
val baseSp = (cellSize / parent.resources.displayMetrics.density).toInt()
b.tvAddr.textSize = (baseSp * 0.16f).coerceIn(9f, 14f)
diff --git a/app/src/main/java/com/shuwei/dish/match/base/BaseActivity.kt b/app/src/main/java/com/shuwei/dish/match/base/BaseActivity.kt
index fbc5be1..741a0f1 100644
--- a/app/src/main/java/com/shuwei/dish/match/base/BaseActivity.kt
+++ b/app/src/main/java/com/shuwei/dish/match/base/BaseActivity.kt
@@ -26,6 +26,7 @@ import com.shuwei.dish.match.entity.SeasoningEntity
import com.shuwei.dish.match.net.NetViewModel
import com.shuwei.dish.match.ui.InitActivity
import com.shuwei.dish.match.utils.ActivityManager
+import com.shuwei.dish.match.utils.ext.dp
import com.shuwei.dish.match.utils.ext.gone
import com.shuwei.dish.match.utils.ext.invisible
import com.shuwei.dish.match.utils.ext.visible
@@ -65,6 +66,10 @@ open class BaseActivity : AppCompatActivity() {
// systemBarsBehavior =
// WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE // 滑动时临时显示
// }
+ // 主设备垂直 padding 30dp,子设备 15dp
+ val verticalPadding = if (GlobalData.deviceRole == DeviceRole.MASTER) 30.dp else 10.dp
+ binding.tvLeftTime.setPadding(0, verticalPadding, 0, verticalPadding)
+ binding.tvRightTime.setPadding(0, verticalPadding, 0, verticalPadding)
binding.llTitleBar.gone()
binding.ivBack.setOnClickListener { finish() }
}
diff --git a/app/src/main/java/com/shuwei/dish/match/dialog/CommonDialog.kt b/app/src/main/java/com/shuwei/dish/match/dialog/CommonDialog.kt
index 5776e83..365115f 100644
--- a/app/src/main/java/com/shuwei/dish/match/dialog/CommonDialog.kt
+++ b/app/src/main/java/com/shuwei/dish/match/dialog/CommonDialog.kt
@@ -7,7 +7,11 @@ import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.View
import android.view.Window
+import android.widget.LinearLayout
import androidx.annotation.StyleRes
+import androidx.core.view.updateLayoutParams
+import com.shuwei.dish.match.base.DeviceRole
+import com.shuwei.dish.match.base.GlobalData
import com.shuwei.dish.match.databinding.DialogCommonBinding
import com.shuwei.dish.match.utils.ext.dp
import java.lang.ref.WeakReference
@@ -55,12 +59,17 @@ open class CommonDialog(
binding = DialogCommonBinding.inflate(layoutInflater)
setContentView(binding.root)
window?.run {
+ val screenWidth = context.resources.displayMetrics.widthPixels
+ val isMaster = GlobalData.deviceRole == DeviceRole.MASTER
attributes = attributes.apply {
- width = 660.dp
+ width = if (isMaster) (screenWidth * 0.72f).toInt() else (screenWidth * 0.60f).toInt()
}
setBackgroundDrawable(ColorDrawable())
setCancelable(false)
}
+ val verticalMargin = if (GlobalData.deviceRole == DeviceRole.MASTER) 35.dp else 20.dp
+ binding.tvTitle.updateLayoutParams { topMargin = verticalMargin }
+ binding.tvContent.updateLayoutParams { bottomMargin = verticalMargin }
binding.btnLeft.setOnClickListener {
negativeClick?.invoke()
dismiss()
diff --git a/app/src/main/java/com/shuwei/dish/match/dialog/LoadingDialog.kt b/app/src/main/java/com/shuwei/dish/match/dialog/LoadingDialog.kt
index d004dcb..f893ce8 100644
--- a/app/src/main/java/com/shuwei/dish/match/dialog/LoadingDialog.kt
+++ b/app/src/main/java/com/shuwei/dish/match/dialog/LoadingDialog.kt
@@ -66,10 +66,10 @@ class LoadingDialog(
setCancelable(true)
window?.run {
setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
- setFlags(
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- );
+// setFlags(
+// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+// );
}
setOnDismissListener {
onDismiss()
diff --git a/app/src/main/java/com/shuwei/dish/match/ui/FoodRecognizeActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/FoodRecognizeActivity.kt
new file mode 100644
index 0000000..fc0df2b
--- /dev/null
+++ b/app/src/main/java/com/shuwei/dish/match/ui/FoodRecognizeActivity.kt
@@ -0,0 +1,213 @@
+package com.shuwei.dish.match.ui
+
+import android.annotation.SuppressLint
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import androidx.recyclerview.widget.GridLayoutManager
+import com.shuwei.dish.match.adapter.GoodsInfoSearchAdapter
+import com.shuwei.dish.match.base.BaseActivity
+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
+import com.shuwei.dish.match.utils.ext.visible
+
+/**
+ * 食材识别结果页面
+ * 展示识别到的食材照片、食材列表、选中食材名称及秤1重量
+ */
+class FoodRecognizeActivity : BaseActivity() {
+
+ companion object {
+ private const val TAG = "FoodRecognizeActivity"
+ private const val EXTRA_IMAGE_URI = "extra_image_uri"
+ const val EXTRA_SELECTED_ITEM = "extra_selected_item"
+
+ /** 内存缓存食材列表,避免通过 Intent 序列化传递大数据 */
+ private var pendingGoodsList: ArrayList? = null
+
+ /**
+ * 启动本页面,通过 onResult 回调返回用户选中的食材
+ * @param activity 宿主 Activity
+ * @param imageUri 食材照片路径(可为空)
+ * @param goodsList 识别到的食材列表
+ * @param onResult 页面关闭后回调,携带用户选中的食材(未选中时为 null)
+ */
+ fun start(
+ activity: BaseActivity,
+ imageUri: String? = null,
+ goodsList: ArrayList = arrayListOf(),
+ onResult: (CookFoodGoodsEntity?) -> Unit = {}
+ ) {
+ pendingGoodsList = goodsList
+ val intent = Intent(activity, FoodRecognizeActivity::class.java).apply {
+ putExtra(EXTRA_IMAGE_URI, imageUri)
+ }
+ activity.startActivity(intent) { resultIntent ->
+ @Suppress("DEPRECATION")
+ val item = resultIntent?.getSerializableExtra(EXTRA_SELECTED_ITEM) as? CookFoodGoodsEntity
+ onResult(item)
+ }
+ }
+
+ /**
+ * 关闭当前识别页面,并将选中食材通过 Activity Result 回传
+ * 由外部(如重量归零时)调用
+ */
+ fun close() {
+ ActivityManager.getActivityStack()
+ .filterIsInstance()
+ .lastOrNull()
+ ?.finishWithResult()
+ }
+ }
+
+ private lateinit var binding: ActivityFoodRecognizeBinding
+
+ private val list = mutableListOf()
+
+ private val adapter = GoodsInfoSearchAdapter(list).apply {
+ setOnItemClickListener { _, _, position ->
+ // 更新选中状态
+ list.forEachIndexed { i, item -> item.isClicked = (i == position) }
+ notifyDataSetChanged()
+ // 同步更新选中食材名称
+ binding.tvSelectedFood.text = list[position].goodsName ?: "-"
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityFoodRecognizeBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ setHeaderBackground()
+
+ // 显示标题栏
+ setTitleBar(titleBarAction = { it.visible() }, titleAction = { it.text = "食材识别" })
+
+ initData()
+ initRecyclerView()
+ initClickListeners()
+ registerWeightListener()
+ }
+
+ /**
+ * 从 Intent 读取数据并初始化页面
+ */
+ private fun initData() {
+ // 加载食材照片
+ val imageUri = intent.getStringExtra(EXTRA_IMAGE_URI)
+ if (!imageUri.isNullOrBlank()) {
+ binding.ivFoodPhoto.setImageURI(Uri.parse(imageUri))
+ }
+
+ // 取出内存缓存的食材列表,取用后立即清空
+ val goodsList = pendingGoodsList?.also { pendingGoodsList = null }
+ if (!goodsList.isNullOrEmpty()) {
+ list.addAll(goodsList)
+ // 默认选中第一项
+ list[0].isClicked = true
+ binding.tvSelectedFood.text = list[0].goodsName ?: "-"
+ }
+ }
+
+ /**
+ * 初始化 RecyclerView
+ */
+ private fun initRecyclerView() {
+ binding.recyclerView.run {
+ layoutManager = GridLayoutManager(this@FoodRecognizeActivity, 2)
+ adapter = this@FoodRecognizeActivity.adapter
+ }
+ updateListVisibility()
+ }
+
+ /**
+ * 根据列表是否有数据控制 RecyclerView 显隐
+ */
+ private fun updateListVisibility() {
+ if (list.isEmpty()) {
+ binding.recyclerView.gone()
+ } else {
+ binding.recyclerView.visible()
+ }
+ }
+
+ /**
+ * 初始化按钮点击事件
+ */
+ private fun initClickListeners() {
+ // 更换食材:打开食材搜索弹窗
+ binding.btnChangeFood.clickWithDebounce {
+ val currentName = if (list.isNotEmpty()) list.firstOrNull { it.isClicked }?.goodsName else null
+ FoodSearchDialog(
+ activity = this,
+ defGoodsName = currentName,
+ onItemSelected = { item ->
+ // 用搜索结果替换当前选中食材
+ updateSelectedFood(item)
+ }
+ ).show()
+ }
+
+ // 取消:关闭页面
+ binding.btnCancel.setOnClickListener { finish() }
+ }
+
+ /**
+ * 用新食材替换当前选中项(或追加到列表首位)
+ */
+ @SuppressLint("NotifyDataSetChanged")
+ private fun updateSelectedFood(item: CookFoodGoodsEntity) {
+ item.isClicked = true
+ val clickedIndex = list.indexOfFirst { it.isClicked && it.goodsId != item.goodsId }
+ if (clickedIndex >= 0) {
+ list[clickedIndex].isClicked = false
+ }
+ // 若列表中已存在则直接选中,否则插入到首位
+ val existIndex = list.indexOfFirst { it.goodsId == item.goodsId }
+ if (existIndex >= 0) {
+ list.forEachIndexed { i, e -> e.isClicked = (i == existIndex) }
+ } else {
+ list.add(0, item)
+ }
+ adapter.notifyDataSetChanged()
+ binding.tvSelectedFood.text = item.goodsName ?: "-"
+ updateListVisibility()
+ }
+
+ /**
+ * 注册秤1重量监听
+ */
+ private fun registerWeightListener() {
+ WeightUtil.addWeightListener(TAG) { address, _, weight ->
+ if (address == AddressUtil.ONE) {
+ runOnUiThread {
+ binding.tvWeight.text = "$weight g"
+ }
+ }
+ }
+ }
+
+ /**
+ * 携带当前选中食材关闭页面
+ */
+ private fun finishWithResult() {
+ val selected = list.firstOrNull { it.isClicked }
+ val intent = Intent().apply {
+ selected?.let { putExtra(EXTRA_SELECTED_ITEM, it) }
+ }
+ setResult(RESULT_OK, intent)
+ finish()
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ WeightUtil.removeWeightListener(TAG)
+ }
+}
diff --git a/app/src/main/java/com/shuwei/dish/match/ui/PrepareCookActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/PrepareCookActivity.kt
index b63eda9..d3f9b54 100644
--- a/app/src/main/java/com/shuwei/dish/match/ui/PrepareCookActivity.kt
+++ b/app/src/main/java/com/shuwei/dish/match/ui/PrepareCookActivity.kt
@@ -12,7 +12,6 @@ import com.shuwei.dish.match.adapter.DishPartAdapter
import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.databinding.ActivityPrepareCookBinding
import com.shuwei.dish.match.dialog.CommonDialog
-import com.shuwei.dish.match.dialog.FoodRecognizeDialog
import com.shuwei.dish.match.dialog.FoodSearchDialog
import com.shuwei.dish.match.entity.CookFoodEntity
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
@@ -158,6 +157,10 @@ class PrepareCookActivity : BaseActivity() {
}
private val isTakingPhoto = AtomicBoolean(false)
+ private var isRecognizeOpen = false
+ // 页面可见时允许识别,onPause 后禁止,onResume 后恢复
+ private var isRecognizeEnabled = true
+ private var lastPhotoUri: Uri? = null
private var lastWeight = 0.0
private var currentWeight = 0.0
@@ -168,10 +171,11 @@ class PrepareCookActivity : BaseActivity() {
private fun recognizeFood(weight: Double) {
this.currentWeight = weight
if (weight < WEIGHT_RECOGNIZE_VALUE) {
- if (recognizeDialog.isShowing) recognizeDialog.dismiss()
+ isRecognizeOpen = false
+ FoodRecognizeActivity.close()
return
}
- if (isTakingPhoto.get() || recognizeDialog.isShowing || abs(lastWeight - weight) <= WEIGHT_CHANGE_VALUE) return
+ if (!isRecognizeEnabled || isTakingPhoto.get() || isRecognizeOpen || abs(lastWeight - weight) <= WEIGHT_CHANGE_VALUE) return
isTakingPhoto.set(true)
cameraUtils.takePhoto(
succCallback = cameraSuccessCallback,
@@ -194,9 +198,13 @@ class PrepareCookActivity : BaseActivity() {
// materialType =
isNewDishType = true
isOriginalData = false
+ useWeight = lastWeight
+ isSetFinished = true
})
- onFoodItemClick(list.size - 1)
- binding.rvDishPartList.smoothScrollToPosition(list.size - 1)
+ val positon = list.size - 1
+ dishPartAdapter.notifyItemInserted(positon)
+ onFoodItemClick(positon)
+ binding.rvDishPartList.smoothScrollToPosition(positon)
}
/**
@@ -298,18 +306,24 @@ class PrepareCookActivity : BaseActivity() {
private var firstReqSize = 0
private val list = mutableListOf()
- private val recognizeDialog by lazy {
- FoodRecognizeDialog(
- activity = this,
- onDismiss = { isTakingPhoto.set(false) },
- onItemSelected = foodSelectCallback
- )
- }
private val dishPartAdapter by lazy {
DishPartAdapter(list).apply {
setOnItemClickListener { _, _, positon ->
onFoodItemClick(positon)
}
+ addOnItemChildClickListener(R.id.ivClearIcon) { _, _, positon ->
+ if (list[positon].isOriginalData) {
+ list[positon].run {
+ isNewDishType = false
+ isOriginalData = false
+ useWeight = 0.0
+ isSetFinished = false
+ }
+ notifyItemChanged(positon)
+ return@addOnItemChildClickListener
+ }
+ removeAt(positon)
+ }
}
}
@@ -388,11 +402,14 @@ class PrepareCookActivity : BaseActivity() {
override fun onResume() {
super.onResume()
+ isRecognizeEnabled = true
+ isRecognizeOpen = false
cameraUtils.bind()
}
override fun onPause() {
super.onPause()
+ isRecognizeEnabled = false
cameraUtils.unbind()
}
@@ -438,6 +455,7 @@ class PrepareCookActivity : BaseActivity() {
*/
private val cameraSuccessCallback: (Uri) -> Unit = { uri ->
Log.d(TAG, "takePhoto success")
+ lastPhotoUri = uri
lifecycleScope.launch {
withContext(Dispatchers.IO) {
val bitmap = ImageUtil.uriToBitmap(this@PrepareCookActivity, uri)
@@ -485,7 +503,13 @@ class PrepareCookActivity : BaseActivity() {
foodId = index.toString()
))
}
- recognizeDialog.loadData(foodList)
+ FoodRecognizeActivity.start(
+ activity = this,
+ imageUri = lastPhotoUri?.toString(),
+ goodsList = ArrayList(foodList),
+ onResult = { item -> item?.let { foodSelectCallback(it) } }
+ )
+ isRecognizeOpen = true
isTakingPhoto.set(false)
lastWeight = currentWeight
}
diff --git a/app/src/main/java/com/shuwei/dish/match/ui/SlaveActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/SlaveActivity.kt
index 175384c..8496e4b 100644
--- a/app/src/main/java/com/shuwei/dish/match/ui/SlaveActivity.kt
+++ b/app/src/main/java/com/shuwei/dish/match/ui/SlaveActivity.kt
@@ -2,8 +2,10 @@ package com.shuwei.dish.match.ui
import android.annotation.SuppressLint
import android.os.Bundle
+import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
+import android.widget.LinearLayout
import androidx.activity.addCallback
import com.google.android.flexbox.AlignItems
import com.google.android.flexbox.FlexDirection
@@ -74,7 +76,11 @@ class SlaveActivity : BaseActivity() {
val smallSize = usable / 9
val largeSize = (usable - smallSize * 6) / 2
val rvHeight = smallSize * 3 + 3 * 4.dp
- binding.rvScaleList.layoutParams = binding.rvScaleList.layoutParams.also { it.height = rvHeight }
+ binding.rvScaleList.updateLayoutParams {
+ height = rvHeight
+ weight = 0f
+ topMargin = 20.dp
+ }
binding.rvScaleList.layoutManager = FlexboxLayoutManager(this).apply {
flexDirection = FlexDirection.COLUMN
flexWrap = FlexWrap.WRAP
@@ -86,6 +92,7 @@ class SlaveActivity : BaseActivity() {
binding.rvScaleList.adapter = scale22Adapter
}
ScaleDeviceConfig.DEVICE_ID_18 -> {
+ binding.rvScaleList.updateLayoutParams { topMargin = 0 }
binding.rvScaleList.layoutManager = GridLayoutManager(this, 6).apply {
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int) = 1
@@ -97,6 +104,7 @@ class SlaveActivity : BaseActivity() {
binding.rvScaleList.adapter = scale18Adapter
}
else -> {
+ binding.rvScaleList.updateLayoutParams { topMargin = 20.dp }
binding.rvScaleList.layoutManager = LinearLayoutManager(this)
linearAdapter.setOnItemClickListener { _, _, position ->
val item = linearAdapter.items.getOrNull(position) ?: return@setOnItemClickListener
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_dish_clear.png b/app/src/main/res/drawable-xxxhdpi/ic_dish_clear.png
new file mode 100644
index 0000000..970c57a
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_dish_clear.png differ
diff --git a/app/src/main/res/layout/activity_base.xml b/app/src/main/res/layout/activity_base.xml
index 71ed615..cfb267c 100644
--- a/app/src/main/res/layout/activity_base.xml
+++ b/app/src/main/res/layout/activity_base.xml
@@ -21,8 +21,6 @@
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:gravity="center_vertical"
- android:paddingTop="30dp"
- android:paddingBottom="30dp"
android:textColor="@color/black"
android:textSize="28sp"
app:layout_constraintStart_toStartOf="parent"
diff --git a/app/src/main/res/layout/activity_food_recognize.xml b/app/src/main/res/layout/activity_food_recognize.xml
new file mode 100644
index 0000000..6e3a14b
--- /dev/null
+++ b/app/src/main/res/layout/activity_food_recognize.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_slave.xml b/app/src/main/res/layout/activity_slave.xml
index 3fe55c9..47b0e2a 100644
--- a/app/src/main/res/layout/activity_slave.xml
+++ b/app/src/main/res/layout/activity_slave.xml
@@ -4,20 +4,36 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:padding="12dp"
+ android:paddingHorizontal="5dp"
tools:background="@color/bg_color">
-
-
+
+ android:orientation="horizontal"
+ android:gravity="center">
+
+
+
+
+
+
+
+
-
-
-
+
+