功能优化

This commit is contained in:
2025-11-07 14:23:53 +08:00
parent c898cbb3e2
commit 4abc855b36
23 changed files with 737 additions and 63 deletions
@@ -0,0 +1,134 @@
package com.sw.dualscreen.activity
import android.annotation.SuppressLint
import androidx.activity.viewModels
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.LinearLayoutManager
import com.sw.dualscreen.R
import com.sw.dualscreen.adapter.CollectedFoodAdapter
import com.sw.dualscreen.databinding.ActivityCollectedDataBinding
import com.sw.dualscreen.databinding.LayoutEmptySearchBinding
import com.sw.dualscreen.dialog.WarnDialog
import com.sw.dualscreen.ext.addOnActionSearchListener
import com.sw.dualscreen.ext.hideKeyboard
import com.sw.dualscreen.objbox.CollectedFoodBean
import com.sw.dualscreen.objbox.Food
import com.sw.dualscreen.objbox.ObjectBox
import com.sw.dualscreen.viewmodel.BaseViewModel
import com.sw.dualscreen.viewmodel.UserViewModel
import com.sw.plate.utils.ToastUtils
import io.objectbox.Box
import io.objectbox.kotlin.boxFor
import kotlin.getValue
class CollectedDataActivity : BaseActivity<ActivityCollectedDataBinding>() {
private val viewModel by viewModels<UserViewModel>()
override fun getViewModel(): BaseViewModel {
return viewModel
}
override fun inflateViewBinding(): ActivityCollectedDataBinding {
return ActivityCollectedDataBinding.inflate(layoutInflater)
}
private val list: MutableList<CollectedFoodBean> = mutableListOf()
private val adapter by lazy {
CollectedFoodAdapter(list).apply {
isStateViewEnable = true
addOnItemChildClickListener(R.id.ivDeleteFood) { _, _, position ->
deleteGoods(position)
}
}
}
override fun initialize() {
super.initialize()
binding.rvFoodList.let {
it.layoutManager = LinearLayoutManager(this)
it.adapter = adapter
}
binding.ivBack.setOnClickListener { finish() }
binding.ivGoodsSearch.setOnClickListener {
val searchName = binding.etInputGoods.text.toString().trim()
if (searchName.isBlank()) {
ToastUtils.showToast("请输入物品名称")
return@setOnClickListener
}
getCollectGoods(searchName)
}
binding.etInputGoods.let { v ->
v.addOnActionSearchListener {
val searchName = binding.etInputGoods.text.toString().trim()
if (searchName.isBlank()) {
ToastUtils.showToast("请输入物品名称")
return@addOnActionSearchListener
}
getCollectGoods(searchName)
}
v.addTextChangedListener {
if (it.isNullOrBlank()) {
getCollectGoods()
}
}
}
getCollectGoods()
}
private var box: Box<Food>? = null
@SuppressLint("NotifyDataSetChanged")
private fun getCollectGoods(searchName:String?=null) {
if (box == null) {
box = ObjectBox.boxStore.boxFor(Food::class)
}
list.clear()
var queryList = box?.all?.distinctBy { it.name }
if (searchName.isNullOrBlank().not()) {
queryList = queryList?.filter { it.name?.contains(searchName) == true }
}
queryList?.forEach {
list.add(CollectedFoodBean(foodName = it.name))
}
adapter.notifyDataSetChanged()
if (list.isEmpty()) {
loadEmptyView()
}
binding.root.hideKeyboard()
}
private var emptyBinding: LayoutEmptySearchBinding? = null
private fun loadEmptyView() {
if (emptyBinding == null) {
emptyBinding = LayoutEmptySearchBinding.inflate(layoutInflater, binding.rvFoodList, false)
}
emptyBinding?.root?.let { layout ->
layout.setOnClickListener { layout.hideKeyboard() }
adapter.stateView = layout
}
}
private fun deleteGoods(position: Int) {
WarnDialog(
context = context,
content = "请确认是否删除菜品:${list[position].foodName}",
confirmBlock = {
Thread {
runOnUiThread {
showWaitingDialog("加载中……")
}
val name = list[position].foodName
val filterIdList = box?.all?.filter { it.name == name }?.map { it.id }
box?.removeByIds(filterIdList)
runOnUiThread {
hideWaitingDialog()
adapter.removeAt(position)
ToastUtils.showToast("删除成功")
if (list.isEmpty()) {
loadEmptyView()
}
}
}.start()
}).show()
}
}
@@ -5,25 +5,13 @@ import android.content.Context
import android.content.Intent
import android.graphics.Typeface
import android.net.Uri
import android.util.Log
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import com.example.utils.FloatBase64Utils
import com.google.common.util.concurrent.ListenableFuture
import com.sw.dualscreen.R
import com.sw.dualscreen.adapter.FoodCollectionAdapter
import com.sw.dualscreen.adapter.GenericItemAdapter
@@ -31,20 +19,17 @@ import com.sw.dualscreen.adapter.GridSpacingItemDecoration
import com.sw.dualscreen.adapter.dpToPx
import com.sw.dualscreen.databinding.ActivityFoodCollectionBinding
import com.sw.dualscreen.databinding.ItemSearchFoodInfoBinding
import com.sw.dualscreen.databinding.ItemSearchFoodInfoBinding.inflate
import com.sw.dualscreen.ext.clickWithDebounce
import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.objbox.Food
import com.sw.dualscreen.objbox.FoodCollectionBean
import com.sw.dualscreen.objbox.FoodModule
import com.sw.dualscreen.objbox.ObjectBox
import com.sw.dualscreen.presentation.SecondaryScreenPresentation
import com.sw.dualscreen.sdk.SensorScaleUtils
import com.sw.dualscreen.utils.BitmapCropper
import com.sw.dualscreen.utils.BitmapSaver
import com.sw.dualscreen.utils.CameraHelper
import com.sw.dualscreen.utils.Debouncer
import com.sw.dualscreen.utils.GlideUtils
import com.sw.dualscreen.utils.ImageUtil
import com.sw.dualscreen.utils.StorageUtils
import com.sw.dualscreen.view.CustomBottomSheetDialog
import com.sw.dualscreen.viewmodel.BaseViewModel
import com.sw.dualscreen.viewmodel.UserViewModel
import com.sw.plate.utils.ToastUtils
@@ -55,48 +40,36 @@ import org.pytorch.IValue
import org.pytorch.Module
import org.pytorch.torchvision.TensorImageUtils
import timber.log.Timber
import java.io.File
import java.util.concurrent.Executors
class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
private val TAG = "FoodCollectionActivity"
private var imageCapture: ImageCapture? = null
private var isAnalyzing = true // 控制是否进行图像分析
private var imageAnalysis: ImageAnalysis? = null
private var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>? = null
private var presentation: SecondaryScreenPresentation? = null
private val executor = Executors.newSingleThreadExecutor()
private val viewModel by viewModels<UserViewModel>()
private var selectedFoodId: String? = ""
private var selectedFoodName: String? = ""
private var bottomSheetDialog: CustomBottomSheetDialog? = null
private val NO_MEAN_RGB = floatArrayOf(0.0f, 0.0f, 0.0f)
private val NO_STD_RGB = floatArrayOf(1.0f, 1.0f, 1.0f)
private var module: Module? = null
private var box: Box<Food>? = null
private val list: MutableList<FoodCollectionBean> = mutableListOf()
private val foodCollectionList: MutableList<FoodCollectionBean> = mutableListOf()
private lateinit var cameraHelper: CameraHelper
private lateinit var foodAdapter: GenericItemAdapter<FoodInfo, ItemSearchFoodInfoBinding>
private val debouncer = Debouncer(2000)
private val adapter: FoodCollectionAdapter by lazy {
FoodCollectionAdapter(list).apply {
setOnItemClickListener { _, _, position ->
if (list[position].isShowCamera) {
// takePhoto()
cameraHelper.openCamera()
}
}
private val collectionAdapter: FoodCollectionAdapter by lazy {
FoodCollectionAdapter(foodCollectionList).apply {
// setOnItemClickListener { _, _, position ->
// if (list[position].isShowCamera) {
//// takePhoto()
// cameraHelper.openCamera()
// }
// }
addOnItemChildClickListener(R.id.ivDelete) { _, _, position ->
list.removeAt(position)
adapter.notifyItemRemoved(position)
adapter.notifyItemRangeChanged(position, list.size)
collectionAdapter.notifyItemRemoved(position)
collectionAdapter.notifyItemRangeChanged(position, list.size)
}
}
}
@@ -109,6 +82,30 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
return ActivityFoodCollectionBinding.inflate(layoutInflater)
}
private fun cameraCallback(uri:Uri){
val index = foodCollectionList.indexOfFirst { it.bitmap == null }
if (index == -1) {
ToastUtils.showToast("每次只允许保存6条数据")
return
}
ImageUtil.uriToBitmap(this, uri)?.let { bitmap ->
val cropBitmap = BitmapCropper.cropCenter(
original = bitmap,
targetWidth = 1300, targetHeight = 900,
//offsetX = 30, offsetY = 100
)
val file = BitmapSaver.saveToAppFilesDir(
cropBitmap, this, "IMG_CROP_${System.currentTimeMillis()}.jpg"
)
Timber.d("${this.javaClass.simpleName}-cameraCallback-裁剪bitmap保存文件路径:${file?.absolutePath}")
foodCollectionList[index].let {
it.bitmap = cropBitmap
it.isShowCamera = false
}
collectionAdapter.notifyItemChanged(index)
}
}
override fun initialize() {
// 初始化 CameraHelper
@@ -117,14 +114,14 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
caller = this,
authority = "${packageName}.fileprovider"
) { uri, path ->
if (list.size < 6) {
val insertIndex = if (list.isEmpty()) 0 else list.size - 1
list.add(insertIndex, FoodCollectionBean(imageUri = uri))
} else {
list[list.size - 1] = FoodCollectionBean(imageUri = uri)
}
adapter.notifyDataSetChanged()
//if (foodCollectionList.size < 6) {
// val insertIndex = if (foodCollectionList.isEmpty()) 0 else foodCollectionList.size - 1
// foodCollectionList.add(insertIndex, FoodCollectionBean(imageUri = uri))
//} else {
// foodCollectionList[foodCollectionList.size - 1] = FoodCollectionBean(imageUri = uri)
//}
//collectionAdapter.notifyDataSetChanged()
cameraCallback(uri)
}
binding.ivBack.setOnClickListener {
@@ -132,10 +129,10 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
startActivity(intent)
finish()
}
list.add(FoodCollectionBean(isShowCamera = true))
foodCollectionList.add(FoodCollectionBean(isShowCamera = true))
binding.rvFoodCollection.let {
it.layoutManager = GridLayoutManager(this, 3, GridLayoutManager.VERTICAL, false)
it.adapter = adapter
it.adapter = collectionAdapter
}
binding.btnFoodSearch.setOnClickListener {
@@ -147,7 +144,7 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
Toast.makeText(this, "请选择菜品名称", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
if (list.size <= 1) {
if (foodCollectionList.size <= 1) {
Toast.makeText(this, "请拍摄菜品照片", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
@@ -166,6 +163,18 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
}
}
binding.btnCollectedGoods.setOnClickListener {
}
binding.btnTakePhoto.clickWithDebounce {
val count = foodCollectionList.count { it.bitmap != null }
if (count == 6) {
ToastUtils.showToast("每次只允许保存6条数据")
return@clickWithDebounce
}
cameraHelper.openCamera()
}
binding.btnClearData.setOnClickListener { clearData() }
foodAdapter = createAdapter()
binding.recyclerview.layoutManager = GridLayoutManager(context, 2)
// 添加间距装饰(12dp)
@@ -177,14 +186,13 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
)
)
binding.recyclerview.adapter = foodAdapter
}
private fun vectorThread() {
//Loading.show(this)
showWaitingDialog("加载中……")
Thread {
list.forEachIndexed { index, it ->
foodCollectionList.forEachIndexed { index, it ->
if (!it.isShowCamera)
image2VectorTask(it.imageUri!!, index)
@@ -219,7 +227,7 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
Timber.tag("mzf1").e(base64Str)
val imageVector = outputTensor.dataAsFloatArray
box?.put(Food(name = selectedFoodName, foodIdx = 0, foodVector = imageVector))
list[position].let {
foodCollectionList[position].let {
it.imageVector = imageVector
it.isFinish = true
@@ -233,7 +241,7 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
)
}
runOnUiThread {
adapter.notifyItemChanged(position)
collectionAdapter.notifyItemChanged(position)
}
Timber.tag("registerDataChange").e("box.all.size=${box?.all?.size}")
}
@@ -286,4 +294,21 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
}
)
}
private var clickIndex = -1
@SuppressLint("NotifyDataSetChanged")
private fun clearData() {
foodCollectionList.forEach {
it.bitmap = null
it.isShowCamera = true
it.isFinish = false
}
collectionAdapter.notifyDataSetChanged()
clickIndex = -1
binding.editFoodName.setText("")
foodAdapter.updateData(mutableListOf())
foodAdapter.notifyDataSetChanged()
//loadEmptyView()
}
}