功能优化

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
+2 -2
View File
@@ -4,10 +4,10 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-08-05T03:50:06.075639Z"> <DropdownSelection timestamp="2025-10-16T11:00:08.044723900Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=2FD2511004054749" /> <DeviceId pluginId="Default" identifier="serial=192.168.1.233:5555;connection=5022ef29" />
</handle> </handle>
</Target> </Target>
</DropdownSelection> </DropdownSelection>
+1 -1
View File
@@ -1,6 +1,6 @@
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="zulu-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">
+1
View File
@@ -56,6 +56,7 @@
android:exported="true"> android:exported="true">
</activity> </activity>
<activity android:name="com.sw.dualscreen.activity.CollectedDataActivity" />
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
@@ -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.content.Intent
import android.graphics.Typeface import android.graphics.Typeface
import android.net.Uri import android.net.Uri
import android.util.Log
import android.view.View
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels 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.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import com.example.utils.FloatBase64Utils import com.example.utils.FloatBase64Utils
import com.google.common.util.concurrent.ListenableFuture
import com.sw.dualscreen.R import com.sw.dualscreen.R
import com.sw.dualscreen.adapter.FoodCollectionAdapter import com.sw.dualscreen.adapter.FoodCollectionAdapter
import com.sw.dualscreen.adapter.GenericItemAdapter 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.adapter.dpToPx
import com.sw.dualscreen.databinding.ActivityFoodCollectionBinding import com.sw.dualscreen.databinding.ActivityFoodCollectionBinding
import com.sw.dualscreen.databinding.ItemSearchFoodInfoBinding 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.model.response.FoodInfo
import com.sw.dualscreen.objbox.Food import com.sw.dualscreen.objbox.Food
import com.sw.dualscreen.objbox.FoodCollectionBean import com.sw.dualscreen.objbox.FoodCollectionBean
import com.sw.dualscreen.objbox.FoodModule import com.sw.dualscreen.objbox.FoodModule
import com.sw.dualscreen.objbox.ObjectBox import com.sw.dualscreen.objbox.ObjectBox
import com.sw.dualscreen.presentation.SecondaryScreenPresentation import com.sw.dualscreen.utils.BitmapCropper
import com.sw.dualscreen.sdk.SensorScaleUtils import com.sw.dualscreen.utils.BitmapSaver
import com.sw.dualscreen.utils.CameraHelper import com.sw.dualscreen.utils.CameraHelper
import com.sw.dualscreen.utils.Debouncer import com.sw.dualscreen.utils.Debouncer
import com.sw.dualscreen.utils.GlideUtils
import com.sw.dualscreen.utils.ImageUtil 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.BaseViewModel
import com.sw.dualscreen.viewmodel.UserViewModel import com.sw.dualscreen.viewmodel.UserViewModel
import com.sw.plate.utils.ToastUtils import com.sw.plate.utils.ToastUtils
@@ -55,48 +40,36 @@ import org.pytorch.IValue
import org.pytorch.Module import org.pytorch.Module
import org.pytorch.torchvision.TensorImageUtils import org.pytorch.torchvision.TensorImageUtils
import timber.log.Timber import timber.log.Timber
import java.io.File
import java.util.concurrent.Executors
class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() { 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 val viewModel by viewModels<UserViewModel>()
private var selectedFoodId: String? = "" private var selectedFoodId: String? = ""
private var selectedFoodName: 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_MEAN_RGB = floatArrayOf(0.0f, 0.0f, 0.0f)
private val NO_STD_RGB = floatArrayOf(1.0f, 1.0f, 1.0f) private val NO_STD_RGB = floatArrayOf(1.0f, 1.0f, 1.0f)
private var module: Module? = null private var module: Module? = null
private var box: Box<Food>? = 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 cameraHelper: CameraHelper
private lateinit var foodAdapter: GenericItemAdapter<FoodInfo, ItemSearchFoodInfoBinding> private lateinit var foodAdapter: GenericItemAdapter<FoodInfo, ItemSearchFoodInfoBinding>
private val debouncer = Debouncer(2000) private val debouncer = Debouncer(2000)
private val adapter: FoodCollectionAdapter by lazy { private val collectionAdapter: FoodCollectionAdapter by lazy {
FoodCollectionAdapter(list).apply { FoodCollectionAdapter(foodCollectionList).apply {
setOnItemClickListener { _, _, position -> // setOnItemClickListener { _, _, position ->
if (list[position].isShowCamera) { // if (list[position].isShowCamera) {
// takePhoto() //// takePhoto()
cameraHelper.openCamera() // cameraHelper.openCamera()
} // }
} // }
addOnItemChildClickListener(R.id.ivDelete) { _, _, position -> addOnItemChildClickListener(R.id.ivDelete) { _, _, position ->
list.removeAt(position) list.removeAt(position)
adapter.notifyItemRemoved(position) collectionAdapter.notifyItemRemoved(position)
adapter.notifyItemRangeChanged(position, list.size) collectionAdapter.notifyItemRangeChanged(position, list.size)
} }
} }
} }
@@ -109,6 +82,30 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
return ActivityFoodCollectionBinding.inflate(layoutInflater) 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() { override fun initialize() {
// 初始化 CameraHelper // 初始化 CameraHelper
@@ -117,14 +114,14 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
caller = this, caller = this,
authority = "${packageName}.fileprovider" authority = "${packageName}.fileprovider"
) { uri, path -> ) { uri, path ->
if (list.size < 6) { //if (foodCollectionList.size < 6) {
val insertIndex = if (list.isEmpty()) 0 else list.size - 1 // val insertIndex = if (foodCollectionList.isEmpty()) 0 else foodCollectionList.size - 1
list.add(insertIndex, FoodCollectionBean(imageUri = uri)) // foodCollectionList.add(insertIndex, FoodCollectionBean(imageUri = uri))
} else { //} else {
list[list.size - 1] = FoodCollectionBean(imageUri = uri) // foodCollectionList[foodCollectionList.size - 1] = FoodCollectionBean(imageUri = uri)
} //}
//collectionAdapter.notifyDataSetChanged()
adapter.notifyDataSetChanged() cameraCallback(uri)
} }
binding.ivBack.setOnClickListener { binding.ivBack.setOnClickListener {
@@ -132,10 +129,10 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
startActivity(intent) startActivity(intent)
finish() finish()
} }
list.add(FoodCollectionBean(isShowCamera = true)) foodCollectionList.add(FoodCollectionBean(isShowCamera = true))
binding.rvFoodCollection.let { binding.rvFoodCollection.let {
it.layoutManager = GridLayoutManager(this, 3, GridLayoutManager.VERTICAL, false) it.layoutManager = GridLayoutManager(this, 3, GridLayoutManager.VERTICAL, false)
it.adapter = adapter it.adapter = collectionAdapter
} }
binding.btnFoodSearch.setOnClickListener { binding.btnFoodSearch.setOnClickListener {
@@ -147,7 +144,7 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
Toast.makeText(this, "请选择菜品名称", Toast.LENGTH_SHORT).show() Toast.makeText(this, "请选择菜品名称", Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} }
if (list.size <= 1) { if (foodCollectionList.size <= 1) {
Toast.makeText(this, "请拍摄菜品照片", Toast.LENGTH_SHORT).show() Toast.makeText(this, "请拍摄菜品照片", Toast.LENGTH_SHORT).show()
return@setOnClickListener 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() foodAdapter = createAdapter()
binding.recyclerview.layoutManager = GridLayoutManager(context, 2) binding.recyclerview.layoutManager = GridLayoutManager(context, 2)
// 添加间距装饰(12dp) // 添加间距装饰(12dp)
@@ -177,14 +186,13 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
) )
) )
binding.recyclerview.adapter = foodAdapter binding.recyclerview.adapter = foodAdapter
} }
private fun vectorThread() { private fun vectorThread() {
//Loading.show(this) //Loading.show(this)
showWaitingDialog("加载中……") showWaitingDialog("加载中……")
Thread { Thread {
list.forEachIndexed { index, it -> foodCollectionList.forEachIndexed { index, it ->
if (!it.isShowCamera) if (!it.isShowCamera)
image2VectorTask(it.imageUri!!, index) image2VectorTask(it.imageUri!!, index)
@@ -219,7 +227,7 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
Timber.tag("mzf1").e(base64Str) Timber.tag("mzf1").e(base64Str)
val imageVector = outputTensor.dataAsFloatArray val imageVector = outputTensor.dataAsFloatArray
box?.put(Food(name = selectedFoodName, foodIdx = 0, foodVector = imageVector)) box?.put(Food(name = selectedFoodName, foodIdx = 0, foodVector = imageVector))
list[position].let { foodCollectionList[position].let {
it.imageVector = imageVector it.imageVector = imageVector
it.isFinish = true it.isFinish = true
@@ -233,7 +241,7 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
) )
} }
runOnUiThread { runOnUiThread {
adapter.notifyItemChanged(position) collectionAdapter.notifyItemChanged(position)
} }
Timber.tag("registerDataChange").e("box.all.size=${box?.all?.size}") 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()
}
} }
@@ -0,0 +1,34 @@
package com.sw.dualscreen.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.sw.dualscreen.R
import com.sw.dualscreen.databinding.ListItemCollectedDataBinding
import com.sw.dualscreen.databinding.ListItemFoodCollectionBinding
import com.sw.dualscreen.objbox.CollectedFoodBean
import com.sw.dualscreen.objbox.FoodCollectionBean
class CollectedFoodAdapter (var list: MutableList<CollectedFoodBean>) :
BaseQuickAdapter<CollectedFoodBean, CollectedFoodAdapter.VH>(list) {
inner class VH(var binding: ListItemCollectedDataBinding) : QuickViewHolder(binding.root)
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
val inflater = LayoutInflater.from(context)
val binding = ListItemCollectedDataBinding.inflate(inflater, parent, false)
return VH(binding)
}
override fun onBindViewHolder(holder: VH, position: Int, item: CollectedFoodBean?) {
holder.binding.tvFoodName.text = item?.foodName
holder.binding.divider.run {
visibility = if (position == list.size - 1) View.GONE else View.VISIBLE
}
}
}
@@ -0,0 +1,48 @@
package com.sw.dualscreen.dialog
import android.app.Dialog
import android.content.Context
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.Gravity
import android.view.View
import com.sw.dualscreen.R
import com.sw.dualscreen.ext.dp
import com.sw.dualscreen.ext.hideKeyboard
abstract class BaseDialog(
context: Context,
var defWidth: Int = 400.dp,
var defHeight: Int = 300.dp
) : Dialog(context, R.style.DialogTheme) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val rootView = getRootView()
setContentView(rootView)
setCancelable(false)
setCanceledOnTouchOutside(false)
window?.apply {
setLayout(defWidth, defHeight)
setBackgroundDrawable(ColorDrawable())
setGravity(Gravity.CENTER)
}
rootView.setOnClickListener { it.hideKeyboard() }
initView()
}
abstract fun getRootView(): View
abstract fun initView()
// fun getReceiptActivity(): GoodsListActivity? {
// var ctx = context
// while (ctx is ContextWrapper) {
// ctx = ctx.baseContext
// if (ctx is GoodsListActivity) {
// return ctx
// }
// }
// return null
// }
}
@@ -0,0 +1,35 @@
package com.sw.dualscreen.dialog
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import com.sw.dualscreen.databinding.DialogWarnBinding
class WarnDialog(
context: Context,
var content:String,
var cancelBlock: () -> Unit = {},
var confirmBlock: () -> Unit = {},
) : BaseDialog(context = context) {
private lateinit var binding: DialogWarnBinding
override fun getRootView(): View {
val inflater = LayoutInflater.from(context)
binding = DialogWarnBinding.inflate(inflater)
return binding.root
}
override fun initView() {
binding.tvWarnContent.text = content
binding.btnCancel.setOnClickListener {
cancelBlock()
dismiss()
}
binding.btnConfirm.setOnClickListener {
confirmBlock()
dismiss()
}
}
}
@@ -1,5 +1,6 @@
package com.sw.dualscreen.ext package com.sw.dualscreen.ext
import android.content.Context
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
@@ -7,8 +8,17 @@ import android.graphics.ImageFormat
import android.graphics.Rect import android.graphics.Rect
import android.graphics.YuvImage import android.graphics.YuvImage
import android.util.TypedValue import android.util.TypedValue
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.annotation.Dimension import androidx.annotation.Dimension
import androidx.camera.core.ImageProxy import androidx.camera.core.ImageProxy
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
/** /**
@@ -81,4 +91,31 @@ fun ImageProxy.toSafeBitmap(): Bitmap {
yuvImage.compressToJpeg(Rect(0, 0, this.width, this.height), 100, out) yuvImage.compressToJpeg(Rect(0, 0, this.width, this.height), 100, out)
val imageBytes = out.toByteArray() val imageBytes = out.toByteArray()
return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size) return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
} }
fun View.hideKeyboard() {
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(this.windowToken, 0)
this.clearFocus() // 清除焦点避免键盘再次弹出
}
fun EditText.addOnActionSearchListener(searchCallback: () -> Unit) {
setOnEditorActionListener { v, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
// 处理搜索逻辑
searchCallback()
return@setOnEditorActionListener true // 阻止事件继续传递
}
return@setOnEditorActionListener false
}
}
fun View.clickWithDebounce(delay: Long = 500, action: () -> Unit) {
var job: Job? = null
setOnClickListener {
job?.cancel()
job = CoroutineScope(Dispatchers.Main).launch {
delay(delay)
action()
}
}
}
@@ -23,3 +23,7 @@ data class FoodCollectionBean(
var isShowCamera: Boolean = false, var isShowCamera: Boolean = false,
var isFinish:Boolean = false var isFinish:Boolean = false
) )
data class CollectedFoodBean(
var foodName: String?,
)
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#30000000">
<item>
<shape android:shape="rectangle">
<solid android:color="#FF0032C8" />
<corners android:radius="10dp" />
</shape>
</item>
</ripple>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white"/>
<corners android:radius="12dp"/>
</shape>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#30000000">
<item>
<shape android:shape="rectangle">
<solid android:color="@color/white"/>
<stroke android:color="#FF0032C8" android:width="1dp"/>
<corners android:radius="10dp"/>
</shape>
</item>
</ripple>
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="68dp"
android:layout_marginTop="24dp">
<ImageView
android:id="@+id/ivBack"
android:layout_width="50dp"
android:layout_height="68dp"
android:padding="10dp"
android:src="@drawable/ic_back" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="已采集菜品"
android:textColor="@color/black"
android:textSize="26sp" />
</FrameLayout>
<FrameLayout
android:id="@+id/flSearchBlock"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_marginHorizontal="20dp"
android:background="@drawable/bg_gray">
<EditText
android:id="@+id/etInputGoods"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00000000"
android:gravity="center"
android:hint="输入菜品名称"
android:maxLines="1"
android:paddingStart="10dp"
android:paddingEnd="80dp"
android:textColor="#FF666666"
android:textColorHint="#FFC7C8DC"
android:textSize="30sp"
android:inputType="text"
android:imeOptions="actionSearch" />
<ImageView
android:id="@+id/ivGoodsSearch"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="16dp"
android:padding="14dp"
android:src="@drawable/ic_goods_search" />
</FrameLayout>
<!-- <com.scwang.smart.refresh.layout.SmartRefreshLayout-->
<!-- android:id="@+id/refreshLayout"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="0dp"-->
<!-- android:layout_marginHorizontal="130dp"-->
<!-- android:layout_marginTop="40dp"-->
<!-- android:layout_marginBottom="58dp"-->
<!-- app:layout_constraintBottom_toTopOf="@id/dividerLine"-->
<!-- app:layout_constraintTop_toBottomOf="@id/flSearchBlock">-->
<!-- <com.scwang.smart.refresh.header.ClassicsHeader-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content" />-->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvFoodList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
android:layout_margin="10dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:itemCount="8"
tools:listitem="@layout/list_item_collected_data" />
<!-- <com.scwang.smart.refresh.footer.ClassicsFooter-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content" />-->
<!-- </com.scwang.smart.refresh.layout.SmartRefreshLayout>-->
</LinearLayout>
@@ -27,6 +27,90 @@
android:textSize="26sp" /> android:textSize="26sp" />
</FrameLayout> </FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="10dp">
<FrameLayout
android:id="@+id/flPreview"
android:layout_width="180dp"
android:layout_height="180dp"
android:layout_marginStart="24dp">
<androidx.cardview.widget.CardView
android:id="@+id/flCameraPreview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="2dp"
app:cardCornerRadius="10dp"
app:cardElevation="0dp"/>
<LinearLayout
android:id="@+id/llCameraFlag"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical"
android:visibility="visible">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:scaleType="fitCenter"
android:src="@drawable/ic_camera256"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="图片采集"
android:textColor="#ffb4b4c8"
android:textSize="16sp" />
</LinearLayout>
</FrameLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="20dp"
android:orientation="vertical"
android:gravity="center_vertical">
<Button
android:id="@+id/btnTakePhoto"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="拍照"
android:textColor="#ffffff"
android:textSize="18sp" />
<Button
android:id="@+id/btnClearData"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="清除"
android:layout_marginVertical="5dp"
android:textColor="#ffffff"
android:textSize="18sp" />
<Button
android:id="@+id/btnCollectedGoods"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="查看已采集菜品"
android:textColor="#ffffff"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvFoodCollection" android:id="@+id/rvFoodCollection"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -43,7 +127,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="24dp" android:layout_marginLeft="24dp"
android:layout_marginTop="48dp" android:layout_marginTop="10dp"
android:layout_marginRight="24dp" android:layout_marginRight="24dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal">
@@ -78,7 +162,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginStart="24dp" android:layout_marginStart="24dp"
android:layout_marginTop="24dp" android:layout_marginTop="10dp"
android:layout_marginEnd="24dp" android:layout_marginEnd="24dp"
android:layout_weight="1" /> android:layout_weight="1" />
@@ -87,7 +171,7 @@
android:layout_width="260dp" android:layout_width="260dp"
android:layout_height="60dp" android:layout_height="60dp"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_marginVertical="20dp" android:layout_marginVertical="10dp"
android:text="保存" android:text="保存"
android:textColor="#ffffff" android:textColor="#ffffff"
android:textSize="20sp" /> android:textSize="20sp" />
+56
View File
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="400dp"
android:layout_height="200dp"
android:gravity="center"
android:orientation="vertical"
android:background="@drawable/bg_white_radius12"
tools:ignore="HardcodedText">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/ic_tip_warn"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/tvWarnContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="30dp"
android:layout_marginVertical="20dp"
android:gravity="center"
tools:text="存在未收货的物品,请确认是否放弃收货,若放弃则数据不会保存?"
android:textColor="#ff141428"
android:textSize="18sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/btnCancel"
android:layout_width="100dp"
android:layout_height="40dp"
android:background="@drawable/bg_white_stroke_blue_ripple"
android:text="取消"
android:gravity="center"
android:textColor="#FF0033CC"
android:textSize="20sp" />
<TextView
android:id="@+id/btnConfirm"
android:layout_width="100dp"
android:layout_height="40dp"
android:layout_marginStart="30dp"
android:background="@drawable/bg_blue_ripple"
android:text="确认"
android:gravity="center"
android:textColor="@color/white"
android:textSize="20sp" />
</LinearLayout>
</LinearLayout>
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:ignore="HardcodedText">
<ImageView
android:layout_width="wrap_content"
android:layout_height="80dp"
android:src="@drawable/ic_empty_view"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:layout_marginBottom="39dp"
android:text="暂无该物品"
android:textColor="#ff000000"
android:textSize="30sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="请检查物品名称,或稍后重新搜索"
android:textColor="#ff999999"
android:gravity="center"
android:textSize="26sp" />
</LinearLayout>
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/tvFoodName"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_gravity="center"
android:gravity="center_vertical"
android:layout_marginStart="30dp"
android:ellipsize="end"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/ivDeleteFood"
android:maxLines="1"
android:textColor="#ff666666"
android:textSize="26sp"
tools:text="高杆白菜" />
<ImageView
android:id="@+id/ivDeleteFood"
android:layout_width="80dp"
android:layout_height="80dp"
android:paddingHorizontal="25dp"
android:layout_gravity="end"
android:layout_marginHorizontal="5dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tvFoodName"
app:layout_constraintStart_toEndOf="@id/tvFoodName"
android:src="@drawable/ic_close" />
<com.google.android.material.divider.MaterialDivider
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginHorizontal="30dp"
app:layout_constraintTop_toBottomOf="@id/tvFoodName"
app:dividerColor="#FFE6E6E6"/>
</androidx.constraintlayout.widget.ConstraintLayout>
+19
View File
@@ -130,4 +130,23 @@
<item name="windowNoTitle">true</item> <item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item> <item name="windowActionBar">false</item>
</style> </style>
<style name="DialogTheme" parent="@android:style/Theme.Dialog">
<!-- 边框 -->
<item name="android:windowFrame">@null</item>
<!-- 是否浮现在activity之上 -->
<item name="android:windowIsFloating">true</item>
<!-- 半透明 -->
<item name="android:windowIsTranslucent">true</item>
<!-- 无标题 -->
<item name="android:windowNoTitle">true</item>
<item name="android:background">@android:color/transparent</item>
<!-- 背景透明 -->
<item name="android:windowBackground">@android:color/transparent</item>
<!-- 模糊 -->
<item name="android:backgroundDimEnabled">true</item>
<!-- 遮罩层 -->
<item name="android:backgroundDimAmount">0.5</item>
</style>
</resources> </resources>