diff --git a/app/src/main/java/com/shuwei/dish/match/ui/SettingActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/SettingActivity.kt index 2587ed5..58eee9c 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/SettingActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/SettingActivity.kt @@ -2,33 +2,19 @@ package com.shuwei.dish.match.ui import android.Manifest import android.annotation.SuppressLint +import android.content.Intent import android.content.pm.PackageManager -import android.graphics.Color import android.net.Uri import android.os.Build import android.os.Bundle -import android.view.ViewGroup -import androidx.appcompat.widget.AppCompatButton +import android.provider.Settings import androidx.core.content.ContextCompat -import androidx.core.graphics.toColorInt -import androidx.fragment.app.Fragment -import androidx.camera.view.PreviewView -import androidx.core.view.updateLayoutParams -import com.shuwei.dish.match.R import com.shuwei.dish.match.base.BaseActivity -import com.shuwei.dish.match.base.DeviceRole -import com.shuwei.dish.match.base.GlobalData import com.shuwei.dish.match.databinding.ActivitySettingBinding -import com.shuwei.dish.match.databinding.LayoutCameraPreviewBinding -import com.shuwei.dish.match.ui.fragment.CollectFragment -import com.shuwei.dish.match.ui.fragment.DeviceConfigFragment -import com.shuwei.dish.match.ui.fragment.SeasoningConfigFragment -import com.shuwei.dish.match.utils.CameraUtils +import com.shuwei.dish.match.dialog.CommonDialog import com.shuwei.dish.match.utils.SpTool -import com.shuwei.dish.match.utils.ext.dp +import com.shuwei.dish.match.utils.ext.clickWithDebounce import com.shuwei.dish.match.utils.ext.gone -import com.shuwei.dish.match.utils.ext.startActivity -import com.shuwei.dish.match.utils.ext.toast import com.shuwei.dish.match.utils.ext.visible /** @@ -42,10 +28,10 @@ class SettingActivity : BaseActivity() { } private lateinit var binding: ActivitySettingBinding - private val cameraUtils: CameraUtils by lazy { - CameraUtils(this) - } - private lateinit var previewView: PreviewView +// private val cameraUtils: CameraUtils by lazy { +// CameraUtils(this) +// } +// private lateinit var previewView: PreviewView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -55,104 +41,51 @@ class SettingActivity : BaseActivity() { setTitleBar(titleBarAction = { it.visible() }, titleAction = { - it.text = "设置" + it.text = "设备设置" }, rightIconActon = { it.gone() }) - // 默认显示设备配置Fragment - if (savedInstanceState == null) { - showDeviceConfigFragment() - } + initView() - initUI() +// // 默认显示设备配置Fragment +// if (savedInstanceState == null) { +// showDeviceConfigFragment() +// } +// +// initUI() } - /** - * 初始化UI和事件监听 - */ - private fun initUI() { - // 设备配置按钮点击事件 - binding.btnDeviceConfig.setOnClickListener { - showDeviceConfigFragment() - loadTabStyle(button = binding.btnDeviceConfig, isSelected = true) - loadTabStyle(button = binding.btnSeasoningConfig, isSelected = false) - loadTabStyle(button = binding.btnFoodCollect, isSelected = false) + private fun initView() { + binding.tvCurrentMode.text = when (SpTool.cookMode) { + 0 -> "当前:制作模式" + 1 -> "当前:采样模式" + else -> "当前:未知模式" } - - // 调料配置按钮点击事件 - binding.btnSeasoningConfig.setOnClickListener { - showSeasoningConfigFragment() - - loadTabStyle(button = binding.btnDeviceConfig, isSelected = false) - loadTabStyle(button = binding.btnSeasoningConfig, isSelected = true) - loadTabStyle(button = binding.btnFoodCollect, isSelected = false) + binding.llCookMode.clickWithDebounce { + SingleFragmentActivity.start(this, SingleFragmentActivity.PageType.COOK_MODE) } - - // 菜品采集按钮点击事件 - binding.btnFoodCollect.setOnClickListener { - checkCameraPermissionAndShowFragment() - - loadTabStyle(button = binding.btnDeviceConfig, isSelected = false) - loadTabStyle(button = binding.btnSeasoningConfig, isSelected = false) - loadTabStyle(button = binding.btnFoodCollect, isSelected = true) + binding.llSeasoningConfig.clickWithDebounce { + SingleFragmentActivity.start(this, SingleFragmentActivity.PageType.SEASONING_CONFIG) } - - // 主设备显示「秤数据监控」入口 - if (GlobalData.deviceRole == DeviceRole.MASTER) { - binding.btnScaleMonitor.visible() - binding.btnScaleMonitor.setOnClickListener { - startActivity() - } + binding.llFoodCollect.clickWithDebounce { + checkCameraPermission() } - - // 初始化按钮颜色(默认设备配置为选中状态) - - loadTabStyle(button = binding.btnDeviceConfig, isSelected = true) - loadTabStyle(button = binding.btnSeasoningConfig, isSelected = false) - loadTabStyle(button = binding.btnFoodCollect, isSelected = false) } -// /** 刷新角色按钮文字 */ -// private fun updateRoleButtonText() { -// val roleLabel = if (GlobalData.deviceRole == DeviceRole.MASTER) "主设备" else "子设备" -// binding.btnDeviceRole.text = "设备角色:$roleLabel" -// } - - /** - * 加载顶部Tab按钮样式 - * - * @param button 按钮 - * @param isSelected 是否选中 - */ - private fun loadTabStyle(button: AppCompatButton, isSelected: Boolean) { - button.apply { - if (isSelected){ - // 选中 - setTextColor(Color.WHITE) - textSize = 28f - setTypeface(null, android.graphics.Typeface.BOLD) - background = ContextCompat.getDrawable(this@SettingActivity, R.drawable.bg_btn_underline) - } else { - // 未选中 - setTextColor("#5E7585".toColorInt()) - textSize = 26f - setTypeface(null, android.graphics.Typeface.NORMAL) - background = null - } - } + private fun openCollectPage() { + SingleFragmentActivity.start(this, SingleFragmentActivity.PageType.FOOD_COLLECT) } /** * 检查相机权限并显示 CollectFragment */ @SuppressLint("ObsoleteSdkInt") - private fun checkCameraPermissionAndShowFragment() { - val collectFragment = fragmentList[2] + private fun checkCameraPermission() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - // Android 6.0 以下,直接显示 Fragment - showFragment(collectFragment) + // Android 6.0 以下,直接打开 + openCollectPage() return } if (ContextCompat.checkSelfPermission( @@ -160,109 +93,228 @@ class SettingActivity : BaseActivity() { Manifest.permission.CAMERA ) == PackageManager.PERMISSION_GRANTED ) { - // 权限已授予,直接显示 Fragment - showFragment(collectFragment) + // 权限已授予,直接打开 + openCollectPage() } else { // 权限未授予,申请权限 requestPermission(Manifest.permission.CAMERA) { isGranted -> if (isGranted) { - // 权限已授予,显示 CollectFragment - showFragment(collectFragment) + // 权限已授予,直接打开 + openCollectPage() } else { // 权限被拒绝 - toast("暂无相机权限,无法使用菜品采集功能") + showNotGrantedCameraPermissionDialog() } } } } - /** - * 初始化相机 - * @param container 相机预览容器 - */ - fun initCamera(container: ViewGroup) { - cameraUtils.initCamera() - val previewBinding = - LayoutCameraPreviewBinding.inflate(layoutInflater, container) - previewView = previewBinding.previewView.also { - it.updateLayoutParams { - width = 456.dp - height = 342.dp - } - } - cameraUtils.setPreviewController(previewView) - } - - /** - * Activity 恢复时绑定相机 - */ - override fun onResume() { - super.onResume() - cameraUtils.bind() - } - - /** - * Activity 暂停时解绑相机 - */ - override fun onPause() { - super.onPause() - cameraUtils.unbind() - } - - /** - * 提供给 Fragment 调用的拍照方法 - */ - fun takePhoto(succCallback: (Uri) -> Unit, failCallback: (msg: String) -> Unit = {}) { - cameraUtils.takePhoto(succCallback = succCallback, failCallback = failCallback) - } - - /** - * 显示设备切换Fragment - */ - private fun showDeviceConfigFragment() { - showFragment(fragmentList[0]) - } - - /** - * 显示设调料配置Fragment - */ - private fun showSeasoningConfigFragment() { - showFragment(fragmentList[1]) - } - - private val fragmentList = mutableListOf().apply { - add(DeviceConfigFragment()) - add(SeasoningConfigFragment()) - add(CollectFragment()) - } - - /** - * 显示指定的 Fragment - * 使用 add/show/hide 方式管理 Fragment,避免重复创建 - * 已添加的 Fragment 直接显示,未添加的 Fragment 先添加再显示 - */ - private fun showFragment(fragment: Fragment) { - runCatching { - supportFragmentManager.beginTransaction().apply { - // 如果 Fragment 还未添加到容器中,则添加 - if (!fragment.isAdded) { - add(binding.fragmentContainer.id, fragment) - } else { - // 已添加过,直接显示 - show(fragment) + private fun showNotGrantedCameraPermissionDialog() { + CommonDialog(this) + .setTitle("温馨提示") + .setContent("暂无相机权限,无法使用菜品采集功能,请确认是否同意开始权限?") + .setNegativeButton("不同意") + .setPositiveButton("同意") { + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { + data = Uri.fromParts("package", packageName, null) } - - // 隐藏其他 Fragment - fragmentList.forEach { otherFragment -> - if (otherFragment != fragment && otherFragment.isAdded) { - hide(otherFragment) - } + startActivity(intent) { + checkCameraPermission() } - - commit() } - }.onFailure { - it.printStackTrace() - } + .show() } + +// +// /** +// * 初始化UI和事件监听 +// */ +// private fun initUI() { +// // 设备配置按钮点击事件 +// binding.btnDeviceConfig.setOnClickListener { +// showDeviceConfigFragment() +// +// loadTabStyle(button = binding.btnDeviceConfig, isSelected = true) +// loadTabStyle(button = binding.btnSeasoningConfig, isSelected = false) +// loadTabStyle(button = binding.btnFoodCollect, isSelected = false) +// } +// +// // 调料配置按钮点击事件 +// binding.btnSeasoningConfig.setOnClickListener { +// showSeasoningConfigFragment() +// +// loadTabStyle(button = binding.btnDeviceConfig, isSelected = false) +// loadTabStyle(button = binding.btnSeasoningConfig, isSelected = true) +// loadTabStyle(button = binding.btnFoodCollect, isSelected = false) +// } +// +// // 菜品采集按钮点击事件 +// binding.btnFoodCollect.setOnClickListener { +// checkCameraPermissionAndShowFragment() +// +// loadTabStyle(button = binding.btnDeviceConfig, isSelected = false) +// loadTabStyle(button = binding.btnSeasoningConfig, isSelected = false) +// loadTabStyle(button = binding.btnFoodCollect, isSelected = true) +// } +// +// // 主设备显示「秤数据监控」入口 +// if (GlobalData.deviceRole == DeviceRole.MASTER) { +// binding.btnScaleMonitor.visible() +// binding.btnScaleMonitor.setOnClickListener { +// startActivity() +// } +// } +// +// // 初始化按钮颜色(默认设备配置为选中状态) +// +// loadTabStyle(button = binding.btnDeviceConfig, isSelected = true) +// loadTabStyle(button = binding.btnSeasoningConfig, isSelected = false) +// loadTabStyle(button = binding.btnFoodCollect, isSelected = false) +// } +// +// /** +// * 加载顶部Tab按钮样式 +// * +// * @param button 按钮 +// * @param isSelected 是否选中 +// */ +// private fun loadTabStyle(button: AppCompatButton, isSelected: Boolean) { +// button.apply { +// if (isSelected){ +// // 选中 +// setTextColor(Color.WHITE) +// textSize = 28f +// setTypeface(null, android.graphics.Typeface.BOLD) +// background = ContextCompat.getDrawable(this@SettingActivity, R.drawable.bg_btn_underline) +// } else { +// // 未选中 +// setTextColor("#5E7585".toColorInt()) +// textSize = 26f +// setTypeface(null, android.graphics.Typeface.NORMAL) +// background = null +// } +// } +// } +// +// /** +// * 检查相机权限并显示 CollectFragment +// */ +// @SuppressLint("ObsoleteSdkInt") +// private fun checkCameraPermissionAndShowFragment() { +// val collectFragment = fragmentList[2] +// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { +// // Android 6.0 以下,直接显示 Fragment +// showFragment(collectFragment) +// return +// } +// if (ContextCompat.checkSelfPermission( +// this, +// Manifest.permission.CAMERA +// ) == PackageManager.PERMISSION_GRANTED +// ) { +// // 权限已授予,直接显示 Fragment +// showFragment(collectFragment) +// } else { +// // 权限未授予,申请权限 +// requestPermission(Manifest.permission.CAMERA) { isGranted -> +// if (isGranted) { +// // 权限已授予,显示 CollectFragment +// showFragment(collectFragment) +// } else { +// // 权限被拒绝 +// toast("暂无相机权限,无法使用菜品采集功能") +// } +// } +// } +// } +// +// /** +// * 初始化相机 +// * @param container 相机预览容器 +// */ +// fun initCamera(container: ViewGroup) { +// cameraUtils.initCamera() +// val previewBinding = +// LayoutCameraPreviewBinding.inflate(layoutInflater, container) +// previewView = previewBinding.previewView.also { +// it.updateLayoutParams { +// width = 456.dp +// height = 342.dp +// } +// } +// cameraUtils.setPreviewController(previewView) +// } +// +// /** +// * Activity 恢复时绑定相机 +// */ +// override fun onResume() { +// super.onResume() +// cameraUtils.bind() +// } +// +// /** +// * Activity 暂停时解绑相机 +// */ +// override fun onPause() { +// super.onPause() +// cameraUtils.unbind() +// } +// +// /** +// * 提供给 Fragment 调用的拍照方法 +// */ +// fun takePhoto(succCallback: (Uri) -> Unit, failCallback: (msg: String) -> Unit = {}) { +// cameraUtils.takePhoto(succCallback = succCallback, failCallback = failCallback) +// } +// +// /** +// * 显示设备切换Fragment +// */ +// private fun showDeviceConfigFragment() { +// showFragment(fragmentList[0]) +// } +// +// /** +// * 显示设调料配置Fragment +// */ +// private fun showSeasoningConfigFragment() { +// showFragment(fragmentList[1]) +// } +// +// private val fragmentList = mutableListOf().apply { +// add(DeviceConfigFragment()) +// add(SeasoningConfigFragment()) +// add(CollectFragment()) +// } +// +// /** +// * 显示指定的 Fragment +// * 使用 add/show/hide 方式管理 Fragment,避免重复创建 +// * 已添加的 Fragment 直接显示,未添加的 Fragment 先添加再显示 +// */ +// private fun showFragment(fragment: Fragment) { +// runCatching { +// supportFragmentManager.beginTransaction().apply { +// // 如果 Fragment 还未添加到容器中,则添加 +// if (!fragment.isAdded) { +// add(binding.fragmentContainer.id, fragment) +// } else { +// // 已添加过,直接显示 +// show(fragment) +// } +// +// // 隐藏其他 Fragment +// fragmentList.forEach { otherFragment -> +// if (otherFragment != fragment && otherFragment.isAdded) { +// hide(otherFragment) +// } +// } +// +// commit() +// } +// }.onFailure { +// it.printStackTrace() +// } +// } } diff --git a/app/src/main/java/com/shuwei/dish/match/ui/SingleFragmentActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/SingleFragmentActivity.kt index 5db66e9..fcf44f1 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/SingleFragmentActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/SingleFragmentActivity.kt @@ -17,15 +17,22 @@ import com.shuwei.dish.match.utils.ext.visible */ class SingleFragmentActivity : BaseActivity() { + interface LifecycleCallback { + fun onActivityResume() + fun onActivityPause() + } + /** * 支持的页面类型枚举 * 新增页面时在此追加枚举值,并在 [createFragment] 中补充对应分支 */ enum class PageType { /** 菜品采集页 */ - COLLECT, + FOOD_COLLECT, + /** 设备配置页 */ - DEVICE_CONFIG, + COOK_MODE, + /** 调料配置页 */ SEASONING_CONFIG, } @@ -46,6 +53,11 @@ class SingleFragmentActivity : BaseActivity() { } } + private var lifecycleCallback: LifecycleCallback? = null + fun setLifecycleCallback(callback: LifecycleCallback) { + lifecycleCallback = callback + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHeaderBackground() @@ -80,8 +92,8 @@ class SingleFragmentActivity : BaseActivity() { * 根据页面类型返回对应标题文本 */ private fun getTitleForPage(pageType: PageType): String = when (pageType) { - PageType.COLLECT -> "菜品采集" - PageType.DEVICE_CONFIG -> "设备配置" + PageType.FOOD_COLLECT -> "菜品采集" + PageType.COOK_MODE -> "设备配置" PageType.SEASONING_CONFIG -> "调料配置" } @@ -89,8 +101,19 @@ class SingleFragmentActivity : BaseActivity() { * 根据页面类型创建对应 Fragment 实例 */ private fun createFragment(pageType: PageType): Fragment = when (pageType) { - PageType.COLLECT -> CollectFragment() - PageType.DEVICE_CONFIG -> DeviceConfigFragment() + PageType.FOOD_COLLECT -> CollectFragment() + PageType.COOK_MODE -> DeviceConfigFragment() PageType.SEASONING_CONFIG -> SeasoningConfigFragment() } + + override fun onResume() { + super.onResume() + lifecycleCallback?.onActivityResume() + } + + override fun onPause() { + super.onPause() + lifecycleCallback?.onActivityPause() + } + } diff --git a/app/src/main/java/com/shuwei/dish/match/ui/fragment/CollectFragment.kt b/app/src/main/java/com/shuwei/dish/match/ui/fragment/CollectFragment.kt index d4d01c3..d6d73b4 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/fragment/CollectFragment.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/fragment/CollectFragment.kt @@ -10,24 +10,29 @@ import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager +import androidx.camera.view.PreviewView +import androidx.core.view.updateLayoutParams import androidx.recyclerview.widget.GridLayoutManager import com.shuwei.dish.match.R import com.shuwei.dish.match.adapter.FoodCollectionAdapter import com.shuwei.dish.match.base.BaseFragment import com.shuwei.dish.match.databinding.FragmentCollectBinding +import com.shuwei.dish.match.databinding.LayoutCameraPreviewBinding import com.shuwei.dish.match.dialog.Loading import com.shuwei.dish.match.objbox.FoodCollectionBean import com.shuwei.dish.match.objbox.FoodModule -import com.shuwei.dish.match.ui.SettingActivity +import com.shuwei.dish.match.ui.SingleFragmentActivity import com.shuwei.dish.match.utils.BitmapSaver +import com.shuwei.dish.match.utils.CameraUtils import com.shuwei.dish.match.utils.Debouncer import com.shuwei.dish.match.utils.ImageUtil import com.shuwei.dish.match.utils.LogSaveUtil import com.shuwei.dish.match.utils.ext.clickWithDebounce +import com.shuwei.dish.match.utils.ext.dp import com.shuwei.dish.match.utils.ext.toast @SuppressLint("NotifyDataSetChanged") -class CollectFragment : BaseFragment() { +class CollectFragment : BaseFragment(){ companion object { private const val TAG = "CollectFragment" @@ -58,7 +63,8 @@ class CollectFragment : BaseFragment() { // } // } private val debouncer = Debouncer(2000) - private lateinit var settingActivity: SettingActivity +// private lateinit var settingActivity: SettingActivity + private lateinit var currentActivity: SingleFragmentActivity private val collectionAdapter: FoodCollectionAdapter by lazy { FoodCollectionAdapter(foodCollectionList).apply { @@ -168,7 +174,7 @@ class CollectFragment : BaseFragment() { return } showWaitingDialog("采集中……") - settingActivity.takePhoto(succCallback = cameraCallback, failCallback = { errMsg -> + cameraUtils.takePhoto(succCallback = cameraCallback, failCallback = { errMsg -> hideWaitingDialog() // toast("拍照异常,请重新操作") // if (cameraErrorCount >= 10) { @@ -183,9 +189,18 @@ class CollectFragment : BaseFragment() { } override fun initialize() { - settingActivity = activity as SettingActivity + currentActivity = activity as SingleFragmentActivity // 首次显示 CollectFragment 时初始化相机 - settingActivity.initCamera(binding.flCameraPreview) + initCamera(binding.flCameraPreview) + currentActivity.setLifecycleCallback(object : SingleFragmentActivity.LifecycleCallback { + override fun onActivityResume() { + cameraUtils.bind() + } + + override fun onActivityPause() { + cameraUtils.unbind() + } + }) binding.rvFoodList.let { it.layoutManager = @@ -361,7 +376,7 @@ class CollectFragment : BaseFragment() { private fun searchByFoodName(name: String, block: (String) -> Unit) {} private fun hideWaitingDialog() { - settingActivity.dismissLoading() + currentActivity.dismissLoading() } private fun showWaitingDialog(msg: String) { @@ -369,6 +384,30 @@ class CollectFragment : BaseFragment() { Loading.updateContent(msg) return } - settingActivity.showLoading(msg) + currentActivity.showLoading(msg) } + + private lateinit var previewView: PreviewView + + private val cameraUtils: CameraUtils by lazy { + CameraUtils(requireActivity()) + } + + /** + * 初始化相机 + * @param container 相机预览容器 + */ + fun initCamera(container: ViewGroup) { + cameraUtils.initCamera() + val previewBinding = + LayoutCameraPreviewBinding.inflate(layoutInflater, container) + previewView = previewBinding.previewView.also { + it.updateLayoutParams { + width = 456.dp + height = 342.dp + } + } + cameraUtils.setPreviewController(previewView) + } + } \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/ui/fragment/DeviceConfigFragment.kt b/app/src/main/java/com/shuwei/dish/match/ui/fragment/DeviceConfigFragment.kt index 694b6ee..e0160bb 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/fragment/DeviceConfigFragment.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/fragment/DeviceConfigFragment.kt @@ -1,46 +1,22 @@ package com.shuwei.dish.match.ui.fragment import android.graphics.Typeface -import android.os.Bundle import android.text.SpannableStringBuilder import android.text.style.AbsoluteSizeSpan import android.text.style.ForegroundColorSpan import android.text.style.LineHeightSpan import android.text.style.StyleSpan -import android.util.Log -import android.util.SparseArray import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup -import android.widget.FrameLayout import androidx.core.graphics.toColorInt -import androidx.core.view.forEach -import androidx.lifecycle.ViewModelProvider -import com.shuwei.dish.match.base.BaseApp import com.shuwei.dish.match.base.BaseFragment import com.shuwei.dish.match.databinding.FragmentDeviceConfigBinding -import com.shuwei.dish.match.db.AppRepository -import com.shuwei.dish.match.dialog.SeasoningSearchDialog -import com.shuwei.dish.match.entity.SeasoningEntity -import com.shuwei.dish.match.entity.ResetReasoningRecord -import com.shuwei.dish.match.adapter.TextCellAdapter -import com.shuwei.dish.match.base.BaseActivity -import com.shuwei.dish.match.utils.AddressUtil -import com.shuwei.dish.match.utils.JsonAssetsLoader -import com.shuwei.dish.match.utils.MultiClickDetector -import com.shuwei.dish.match.utils.WeightUtil -import com.shuwei.dish.match.utils.ext.appendText -import com.shuwei.dish.match.utils.ext.buildSpannableString -import com.shuwei.dish.match.utils.ext.dp -import com.shuwei.dish.match.utils.ext.clickWithDebounce -import com.shuwei.dish.match.utils.ext.gone -import com.shuwei.dish.match.utils.ext.toast -import com.shuwei.dish.match.utils.ext.visible -import com.shuwei.dish.match.viewmodel.AppViewModel -import com.shuwei.dish.match.viewmodel.factory.AppFactory import com.shuwei.dish.match.ui.SamplingListActivity import com.shuwei.dish.match.ui.SelectDishActivity import com.shuwei.dish.match.utils.SpTool +import com.shuwei.dish.match.utils.ext.appendText +import com.shuwei.dish.match.utils.ext.buildSpannableString +import com.shuwei.dish.match.utils.ext.dp import com.shuwei.dish.match.utils.ext.startActivity /** @@ -65,10 +41,10 @@ class DeviceConfigFragment : BaseFragment() { // private lateinit var appViewModel: AppViewModel - private val weightArray = SparseArray() - private val addressArray = AddressUtil.getWeighAddressArray() - private var seasoningItems = mutableListOf() - private val detector = MultiClickDetector(targetCount = 10, intervalMs = 800) +// private val weightArray = SparseArray() +// private val addressArray = AddressUtil.getWeighAddressArray() +// private var seasoningItems = mutableListOf() +// private val detector = MultiClickDetector(targetCount = 10, intervalMs = 800) override fun inflateBinding( inflater: LayoutInflater, @@ -77,7 +53,7 @@ class DeviceConfigFragment : BaseFragment() { return FragmentDeviceConfigBinding.inflate(inflater, container, false) } - override fun initialize(){ + override fun initialize() { initViewModel() initUI() // addWeighListener() @@ -140,7 +116,8 @@ class DeviceConfigFragment : BaseFragment() { loadCookSpan() loadSamplingSpan(isClicked = true) activity?.startActivity { - putExtra(SamplingListActivity.IS_CONFIG_PAGE, true) } + putExtra(SamplingListActivity.IS_CONFIG_PAGE, true) + } activity?.finish() } } diff --git a/app/src/main/java/com/shuwei/dish/match/ui/fragment/SeasoningConfigFragment.kt b/app/src/main/java/com/shuwei/dish/match/ui/fragment/SeasoningConfigFragment.kt index 3fdb146..434dc4e 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/fragment/SeasoningConfigFragment.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/fragment/SeasoningConfigFragment.kt @@ -19,12 +19,13 @@ import com.shuwei.dish.match.base.DeviceRole import com.shuwei.dish.match.base.GlobalData import com.shuwei.dish.match.databinding.FragmentSeasoningConfigBinding import com.shuwei.dish.match.dialog.SeasoningSelectDialog +import com.shuwei.dish.match.entity.SeasoningEntity import com.shuwei.dish.match.entity.SeasoningSlotEntity import com.shuwei.dish.match.scale.ScaleDeviceConfig import com.shuwei.dish.match.scale.ScaleEvent import com.shuwei.dish.match.scale.ScaleServiceManager import com.shuwei.dish.match.scale.ScaleEvent.SlotConfig -import com.shuwei.dish.match.ui.SettingActivity +import com.shuwei.dish.match.ui.SingleFragmentActivity import com.shuwei.dish.match.utils.SizeTool import com.shuwei.dish.match.utils.WeightUtil import com.shuwei.dish.match.utils.ext.dp @@ -36,8 +37,10 @@ class SeasoningConfigFragment : BaseFragment() { companion object { private const val TAG = "SeasoningConfigFragment" + /** 重量增加阈值(克),超过此值才触发高亮 */ private const val WEIGHT_DELTA_THRESHOLD = 10.0 + /** 高亮自动恢复时间(毫秒) */ private const val HIGHLIGHT_DURATION_MS = 15_000L } @@ -47,21 +50,28 @@ class SeasoningConfigFragment : BaseFragment() { container: ViewGroup? ) = FragmentSeasoningConfigBinding.inflate(inflater, container, false) - private lateinit var settingActivity: SettingActivity + private lateinit var currentActivity: SingleFragmentActivity // private lateinit var appViewModel: AppViewModel private val adapter22 by lazy { buildAdapter22() } - private val adapter18 by lazy { Seasoning18GridAdapter(showNameOnly = true, leftRightSpace = 20.dp) } + private val adapter18 by lazy { + Seasoning18GridAdapter( + showNameOnly = true, + leftRightSpace = 20.dp + ) + } /** 各秤的上一次重量快照,key 格式 "{deviceId}#{address}",用于计算 delta */ private val weightSnapshot = mutableMapOf() + /** 主线程 Handler,用于延迟清除高亮 */ private val mainHandler = Handler(Looper.getMainLooper()) + /** 当前高亮清除任务,新高亮触发时取消旧任务 */ private var clearHighlightRunnable: Runnable? = null override fun initialize() { - settingActivity = activity as SettingActivity + currentActivity = activity as SingleFragmentActivity // initViewModel() setupRecyclerViews() bindAdapterClicks() @@ -73,62 +83,67 @@ class SeasoningConfigFragment : BaseFragment() { /** 绑定油盆格子点击,弹出调料选择弹窗并保存到 DEVICE_ID_1 */ private fun bindOilSlotClick() { binding.tvOilSlot.setOnClickListener { - SeasoningSelectDialog( - activity = settingActivity, - clickIndex = 0, - onItemSelected = { item -> - binding.tvOilSlot.text = item.goodsName ?: "-" - saveSlotAndBroadcast( - SeasoningSlotEntity( - deviceId = ScaleDeviceConfig.DEVICE_ID_1, - address = 1, - goodsId = item.goodsId, - goodsName = item.goodsName ?: "" - ) + openSeasoningSelectDialog(0) { item -> + binding.tvOilSlot.text = item.goodsName ?: "-" + saveSlotAndBroadcast( + SeasoningSlotEntity( + deviceId = ScaleDeviceConfig.DEVICE_ID_1, + address = 1, + goodsId = item.goodsId, + goodsName = item.goodsName ?: "" ) - } - ).show() + ) + } } } /** 绑定两个 Adapter 的格子点击,弹出调料选择弹窗 */ private fun bindAdapterClicks() { adapter22.onItemClick = { scaleData, position -> - SeasoningSelectDialog( - activity = settingActivity, - clickIndex = position, - onItemSelected = { item -> - adapter22.updateItemName(position, item.goodsName ?: "") - saveSlotAndBroadcast( - SeasoningSlotEntity( - deviceId = ScaleDeviceConfig.DEVICE_ID_22, - address = scaleData.address, - goodsId = item.goodsId, - goodsName = item.goodsName ?: "" - ) + openSeasoningSelectDialog(position) { item -> + adapter22.updateItemName(position, item.goodsName ?: "") + saveSlotAndBroadcast( + SeasoningSlotEntity( + deviceId = ScaleDeviceConfig.DEVICE_ID_22, + address = scaleData.address, + goodsId = item.goodsId, + goodsName = item.goodsName ?: "" ) - } - ).show() + ) + } } adapter18.onItemClick = { scaleData, position -> - SeasoningSelectDialog( - activity = settingActivity, - clickIndex = position, - onItemSelected = { item -> - adapter18.updateItemName(position, item.goodsName ?: "") - saveSlotAndBroadcast( - SeasoningSlotEntity( - deviceId = ScaleDeviceConfig.DEVICE_ID_18, - address = scaleData.address, - goodsId = item.goodsId, - goodsName = item.goodsName ?: "" - ) + openSeasoningSelectDialog(position) { item -> + adapter18.updateItemName(position, item.goodsName ?: "") + saveSlotAndBroadcast( + SeasoningSlotEntity( + deviceId = ScaleDeviceConfig.DEVICE_ID_18, + address = scaleData.address, + goodsId = item.goodsId, + goodsName = item.goodsName ?: "" ) - } - ).show() + ) + } } } + /** + * 打开调料选择弹窗 + * + * @param position 格子位置 + * @param callback 调料选择回调 + */ + private fun openSeasoningSelectDialog( + position: Int, + callback: (item: SeasoningEntity) -> Unit + ) { + SeasoningSelectDialog( + activity = currentActivity, + clickIndex = position, + onItemSelected = callback + ).show() + } + /** * 将单个槽位写入 Room,再读取全量配置广播给子设备 * 仅主设备执行广播,子设备不调用此方法 @@ -153,6 +168,7 @@ class SeasoningConfigFragment : BaseFragment() { private val size by lazy { SizeTool.getFlexLayoutSize(20.dp) } + /** 构建上层(22格)Adapter,需要先计算格子尺寸 */ private fun buildAdapter22(): Seasoning22GridAdapter { return Seasoning22GridAdapter( @@ -205,7 +221,8 @@ class SeasoningConfigFragment : BaseFragment() { } // 加载油盆格子名称(address=1) val oilSlot = slotsOil.firstOrNull { it.address == 1 } - binding.tvOilSlot.text = if (oilSlot != null && oilSlot.goodsName.isNotEmpty()) oilSlot.goodsName else "-" + binding.tvOilSlot.text = + if (oilSlot != null && oilSlot.goodsName.isNotEmpty()) oilSlot.goodsName else "-" } } } @@ -223,7 +240,10 @@ class SeasoningConfigFragment : BaseFragment() { * - STATE_STABLE:无条件更新基准(覆盖取走调料/归零等场景) */ private fun addWeightListener() { - Log.d(TAG, "addWeightListener: 注册监听, deviceId=${GlobalData.deviceId}, role=${GlobalData.deviceRole}") + Log.d( + TAG, + "addWeightListener: 注册监听, deviceId=${GlobalData.deviceId}, role=${GlobalData.deviceRole}" + ) if (GlobalData.deviceRole == DeviceRole.MASTER) { // 主设备:通过 allScales StateFlow 观测所有秤(含远端子设备秤) @@ -243,14 +263,21 @@ class SeasoningConfigFragment : BaseFragment() { return@forEach } val delta = data.weight - baseline - Log.d(TAG, "allScales: key=$key address=${data.address} state=${data.state} weight=${data.weight} baseline=$baseline delta=$delta") + Log.d( + TAG, + "allScales: key=$key address=${data.address} state=${data.state} weight=${data.weight} baseline=$baseline delta=$delta" + ) when { // 仅在秤稳定后判断增量,避免取走物品时手部冲力造成误触发 data.state == WeightUtil.STATE_STABLE && delta > WEIGHT_DELTA_THRESHOLD -> { weightSnapshot[snapshotKey] = data.weight - Log.d(TAG, "allScales: 触发高亮 key=$key address=${data.address} delta=$delta") + Log.d( + TAG, + "allScales: 触发高亮 key=$key address=${data.address} delta=$delta" + ) triggerHighlight(data.deviceId, data.address, delta) } + data.state == WeightUtil.STATE_STABLE -> { weightSnapshot[snapshotKey] = data.weight } @@ -263,7 +290,10 @@ class SeasoningConfigFragment : BaseFragment() { WeightUtil.addWeightListener(TAG) { address, state, weight -> val snapshotKey = "${GlobalData.deviceId}#$address" val baseline = weightSnapshot[snapshotKey] - Log.d(TAG, "onWeight: address=$address state=$state weight=$weight baseline=$baseline") + Log.d( + TAG, + "onWeight: address=$address state=$state weight=$weight baseline=$baseline" + ) if (baseline == null) { weightSnapshot[snapshotKey] = weight @@ -271,7 +301,10 @@ class SeasoningConfigFragment : BaseFragment() { } val delta = weight - baseline - Log.d(TAG, "onWeight: address=$address state=$state weight=$weight baseline=$baseline delta=$delta") + Log.d( + TAG, + "onWeight: address=$address state=$state weight=$weight baseline=$baseline delta=$delta" + ) when { // 仅在秤稳定后判断增量,避免取走物品时手部冲力造成误触发 @@ -280,6 +313,7 @@ class SeasoningConfigFragment : BaseFragment() { Log.d(TAG, "onWeight: 触发高亮 address=$address delta=$delta") triggerHighlight(GlobalData.deviceId, address, delta) } + state == WeightUtil.STATE_STABLE -> { weightSnapshot[snapshotKey] = weight } @@ -302,7 +336,10 @@ class SeasoningConfigFragment : BaseFragment() { val pos18 = if (sourceDeviceId == ScaleDeviceConfig.DEVICE_ID_18) ScaleDeviceConfig.SCALE_ORDER_18.indexOf(address) else -1 - Log.d(TAG, "triggerHighlight: sourceDeviceId=$sourceDeviceId address=$address pos22=$pos22 pos18=$pos18") + Log.d( + TAG, + "triggerHighlight: sourceDeviceId=$sourceDeviceId address=$address pos22=$pos22 pos18=$pos18" + ) // 两个列表都不包含该地址,忽略 if (pos22 < 0 && pos18 < 0) { @@ -318,6 +355,7 @@ class SeasoningConfigFragment : BaseFragment() { adapter22.highlightPosition(pos22) adapter18.clearHighlight() } + else -> { adapter18.highlightPosition(pos18) adapter22.clearHighlight() diff --git a/app/src/main/res/drawable-xxxhdpi/ic_arrow_right2.png b/app/src/main/res/drawable-xxxhdpi/ic_arrow_right2.png new file mode 100644 index 0000000..15be3cf Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_arrow_right2.png differ diff --git a/app/src/main/res/layout/activity_setting.xml b/app/src/main/res/layout/activity_setting.xml index 82faa61..37d2d0b 100644 --- a/app/src/main/res/layout/activity_setting.xml +++ b/app/src/main/res/layout/activity_setting.xml @@ -1,77 +1,165 @@ - + android:gravity="center_vertical" + android:layout_marginHorizontal="32dp" + android:background="@drawable/shape_scale_cell"> - + android:layout_marginStart="32dp" + android:textColor="@color/black333" /> - - - - - - - - - - - - - - + android:layout_marginStart="32dp" + android:textColor="@color/black999" /> + - - + android:layout_height="120dp" + android:orientation="horizontal" + android:gravity="center_vertical" + android:layout_marginHorizontal="32dp" + android:layout_marginTop="32dp" + android:background="@drawable/shape_scale_cell"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +