入库功能增加相机预览及拍照

This commit is contained in:
2025-11-11 11:52:41 +08:00
parent 9927ebe7f1
commit 750d92e2f9
10 changed files with 295 additions and 93 deletions
@@ -67,11 +67,6 @@ class FoodCollectionActivity : ComponentActivity() {
} }
private val collectionAdapter: FoodCollectionAdapter by lazy { private val collectionAdapter: FoodCollectionAdapter by lazy {
FoodCollectionAdapter(collectList).apply { FoodCollectionAdapter(collectList).apply {
// setOnItemClickListener { _, _, position ->
// if (collectList[position].isShowCamera) {
// takePhoto()
// }
// }
addOnItemChildClickListener(R.id.ivDelete) { _, _, position -> addOnItemChildClickListener(R.id.ivDelete) { _, _, position ->
collectList[position].let { collectList[position].let {
it.bitmap = null it.bitmap = null
@@ -79,13 +74,6 @@ class FoodCollectionActivity : ComponentActivity() {
it.isShowCamera = true it.isShowCamera = true
} }
collectionAdapter.notifyItemChanged(position) collectionAdapter.notifyItemChanged(position)
// if (position == collectList.size - 1) {
//
// return@addOnItemChildClickListener
// }
// collectList.removeAt(position)
// adapter.notifyItemRemoved(position)
// adapter.notifyItemRangeChanged(position, collectList.size)
} }
} }
} }
@@ -156,25 +144,6 @@ class FoodCollectionActivity : ComponentActivity() {
binding.btnCollectedGoods.setOnClickListener { binding.btnCollectedGoods.setOnClickListener {
CollectSearchDialog(this@FoodCollectionActivity).show() CollectSearchDialog(this@FoodCollectionActivity).show()
} }
// binding.btnAddDefault.clickWithDebounce {
// Thread{
// FoodModule.initDefFoodData(this) {
// runOnUiThread {
// toast("添加完成")
// }
// }
// }.start()
// }
// binding.btnRemoveDefault.clickWithDebounce {
// initBox()
// val list = box?.all?.filter { it.foodIdx == FoodModule.DEFAULT_FOOD_INDEX }
// if (list.isNullOrEmpty()) {
// toast("不存在默认数据")
// return@clickWithDebounce
// }
// box?.removeByIds(list.map { it.id })
// toast("移除完成")
// }
binding.btnClearData.setOnClickListener { clearData() } binding.btnClearData.setOnClickListener { clearData() }
binding.rvSearch.let { binding.rvSearch.let {
it.layoutManager = GridLayoutManager(this, 2, LinearLayoutManager.VERTICAL, false) it.layoutManager = GridLayoutManager(this, 2, LinearLayoutManager.VERTICAL, false)
@@ -191,49 +160,10 @@ class FoodCollectionActivity : ComponentActivity() {
searchGoods() searchGoods()
} }
} }
// binding.radioGroup.setOnCheckedChangeListener { group, checkedId ->
// if (checkedId == R.id.rbCollected) {
// binding.llSearchInput.gone()
// binding.refreshLayout.let {
// it.setEnableRefresh(false)
// it.setEnableLoadMore(false)
// }
// getCollectedGoods()
// return@setOnCheckedChangeListener
// }
// if (checkedId == R.id.rbSearch) {
// binding.llSearchInput.visible()
// pageNo = 1
// clickIndex = -1
// searchGoodsList.clear()
// searchAdapter.notifyDataSetChanged()
// binding.refreshLayout.let {
// it.setEnableRefresh(true)
// it.setEnableLoadMore(false)
// }
// loadEmptyView()
// return@setOnCheckedChangeListener
// }
// }
// binding.radioGroup.check(R.id.rbCollected)
loadEmptyView() loadEmptyView()
} }
@SuppressLint("NotifyDataSetChanged")
private fun getCollectedGoods() {
searchGoodsList.clear()
initBox()
box?.all?.distinctBy { it.name }?.forEach {
searchGoodsList.add((SearchGoodsInfo.Record(goodsName = it.name)))
}
binding.rvSearch.layoutManager = GridLayoutManager(this, 2, LinearLayoutManager.VERTICAL, false)
searchAdapter.notifyDataSetChanged()
if (searchGoodsList.isEmpty()) {
loadEmptyView()
}
}
private var pageNo = 1 private var pageNo = 1
private var clickIndex = -1 private var clickIndex = -1
@@ -313,12 +243,6 @@ class FoodCollectionActivity : ComponentActivity() {
it.isShowCamera = false it.isShowCamera = false
} }
collectionAdapter.notifyItemChanged(index) collectionAdapter.notifyItemChanged(index)
// if (collectList.size < 6) {
// collectList.add(collectList.size - 1, FoodCollectionBean(bitmap = cropBitmap))
// } else {
// collectList[collectList.size - 1] = FoodCollectionBean(bitmap = cropBitmap)
// }
// adapter.notifyDataSetChanged()
} }
} }
@@ -55,7 +55,11 @@ class GoodsRecognizeDialog(
} }
private fun showGoodsStoreDialog(position: Int) { private fun showGoodsStoreDialog(position: Int) {
GoodsStoreDialog(context, list[position]) { GoodsStoreDialog(
context = context,
funcType = 0,
goods = list[position]
) {
activity?.addGoods(it) activity?.addGoods(it)
activity?.startTime = System.currentTimeMillis() activity?.startTime = System.currentTimeMillis()
dismiss() dismiss()
@@ -63,7 +63,11 @@ class GoodsSearchDialog(
context.toast("请选择物品") context.toast("请选择物品")
return@setOnClickListener return@setOnClickListener
} }
GoodsStoreDialog(context, list[clickIndex]) { GoodsStoreDialog(
context = context,
funcType = 1,
goods = list[clickIndex]
) {
activity?.addGoods(it) activity?.addGoods(it)
activity?.startTime = System.currentTimeMillis() activity?.startTime = System.currentTimeMillis()
activity?.goodsRecognizeDialog?.dismiss() activity?.goodsRecognizeDialog?.dismiss()
@@ -2,6 +2,7 @@ package com.sw.inbound.dialog
import android.content.Context import android.content.Context
import android.graphics.Typeface import android.graphics.Typeface
import android.net.Uri
import android.text.InputType import android.text.InputType
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
@@ -9,6 +10,7 @@ import android.text.style.StyleSpan
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.EditText import android.widget.EditText
import androidx.camera.view.PreviewView
import androidx.core.graphics.toColorInt import androidx.core.graphics.toColorInt
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import coil.load import coil.load
@@ -16,15 +18,29 @@ import com.shuwei.intelligent.shelves.utils.ext.appendText
import com.shuwei.intelligent.shelves.utils.ext.buildSpannableString import com.shuwei.intelligent.shelves.utils.ext.buildSpannableString
import com.sw.inbound.activity.GoodsListActivity import com.sw.inbound.activity.GoodsListActivity
import com.sw.inbound.databinding.DialogGoodsStoreBinding import com.sw.inbound.databinding.DialogGoodsStoreBinding
import com.sw.inbound.databinding.LayoutImagePreviewBinding
import com.sw.inbound.databinding.LayoutTakePhotoBinding
import com.sw.inbound.ext.toFormattedString import com.sw.inbound.ext.toFormattedString
import com.sw.inbound.model.response.DictType import com.sw.inbound.model.response.DictType
import com.sw.inbound.model.response.GoodsInfo import com.sw.inbound.model.response.GoodsInfo
import com.sw.inbound.model.response.SearchGoodsInfo import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.objbox.Food
import com.sw.inbound.objbox.FoodModule
import com.sw.inbound.objbox.ObjectBox
import com.sw.inbound.sdk.SensorScaleUtils import com.sw.inbound.sdk.SensorScaleUtils
import com.sw.inbound.utils.BitmapCropper
import com.sw.inbound.utils.BitmapSaver
import com.sw.inbound.utils.CameraUtils
import com.sw.inbound.utils.ImageUtil
import com.sw.inbound.utils.ext.dp import com.sw.inbound.utils.ext.dp
import com.sw.inbound.utils.ext.gone
import com.sw.inbound.utils.ext.roundedDecimalPlace import com.sw.inbound.utils.ext.roundedDecimalPlace
import com.sw.inbound.utils.ext.toast import com.sw.inbound.utils.ext.toast
import com.sw.inbound.utils.ext.visible
import com.sw.inbound.utils.removeTrailingZeros import com.sw.inbound.utils.removeTrailingZeros
import io.objectbox.Box
import io.objectbox.kotlin.boxFor
import timber.log.Timber
import kotlin.Int import kotlin.Int
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.min import kotlin.math.min
@@ -32,6 +48,7 @@ import kotlin.math.min
class GoodsStoreDialog( class GoodsStoreDialog(
context: Context, context: Context,
var goods: SearchGoodsInfo.Record, var goods: SearchGoodsInfo.Record,
var funcType: Int,
var cancelBlock: () -> Unit = {}, var cancelBlock: () -> Unit = {},
var confirmBlock: (GoodsInfo) -> Unit = {}, var confirmBlock: (GoodsInfo) -> Unit = {},
) : BaseDialog(context) { ) : BaseDialog(context) {
@@ -65,7 +82,9 @@ class GoodsStoreDialog(
setText(goods.goodsName) setText(goods.goodsName)
isEnabled = false isEnabled = false
} }
binding.ivGoodsImage.load(goods.relativeUrl) //binding.ivGoodsImage.load(goods.relativeUrl)
addChildToContainer()
//flContainer
binding.btnCancel.setOnClickListener { binding.btnCancel.setOnClickListener {
cancelBlock() cancelBlock()
dismiss() dismiss()
@@ -101,9 +120,12 @@ class GoodsStoreDialog(
} }
val first = goods.unitVoList!!.first { it.businessUnitId == dictType.id } val first = goods.unitVoList!!.first { it.businessUnitId == dictType.id }
val stringBuilder = StringBuilder() val stringBuilder = StringBuilder()
.append("1").append(dictType.value).append("=").append(first.purchaseValue!!.toInt().toString()).append(goods.kcUnitName) .append("1").append(dictType.value).append("=")
.append(first.purchaseValue!!.toInt().toString())
.append(goods.kcUnitName)
.append("") .append("")
.append("1").append(goods.kcUnitName).append("=").append(first.consumeValue).append("") .append("1").append(goods.kcUnitName).append("=")
.append(first.consumeValue).append("")
binding.tvUnitCalculate.text = stringBuilder.toString() binding.tvUnitCalculate.text = stringBuilder.toString()
//val count = getUnitCount(dictType.id!!) //val count = getUnitCount(dictType.id!!)
//if (count.isBlank() || "0" == count) { //if (count.isBlank() || "0" == count) {
@@ -249,6 +271,10 @@ class GoodsStoreDialog(
} }
private fun confirmInputData() { private fun confirmInputData() {
if (funcType == 1 && imageUri == null) {
context.toast("请采集图片")
return
}
val unit = binding.tvProcurementUnit.text.trim().toString() val unit = binding.tvProcurementUnit.text.trim().toString()
if (unit.isBlank()) { if (unit.isBlank()) {
context.toast("请选择入库单位") context.toast("请选择入库单位")
@@ -288,6 +314,9 @@ class GoodsStoreDialog(
context.toast("入库金额不能为0") context.toast("入库金额不能为0")
return return
} }
if (funcType == 1) {
collectGoodsVector()
}
val id = binding.tvProcurementUnit.tag.toString() val id = binding.tvProcurementUnit.tag.toString()
val unitVo = goods.unitVoList!!.first { it.businessUnitId == id } val unitVo = goods.unitVoList!!.first { it.businessUnitId == id }
confirmBlock( confirmBlock(
@@ -324,4 +353,92 @@ class GoodsStoreDialog(
return count return count
} }
private val cameraUtils: CameraUtils by lazy {
CameraUtils(activity!!)
}
override fun show() {
super.show()
takePhotoBinding?.let {
cameraUtils.setPreviewController(previewView)
cameraUtils.bind()
it.llCameraFlag.run {
postDelayed({
gone()
}, 1000)
}
}
}
private var imageUri: Uri? = null
private val takePhotoCallback: (Uri) -> Unit = { uri ->
takePhotoBinding?.let {
imageUri = uri
it.flCameraPreview.gone()
it.cvImageBg.visible()
it.ivGoodsImage.load(uri)
}
}
private var previewView: PreviewView? = null
private var takePhotoBinding: LayoutTakePhotoBinding? = null
private var imagePreviewBinding: LayoutImagePreviewBinding? = null
private fun addChildToContainer() {
val inflater = LayoutInflater.from(context)
if (funcType == 1) {
//预览及拍照
takePhotoBinding = LayoutTakePhotoBinding.inflate(inflater, binding.flContainer, true)
activity?.unBindCamera()
setOnDismissListener {
cameraUtils.unbind()
}
cameraUtils.initCamera()
takePhotoBinding?.let {
previewView = it.previewView
it.flCameraPreview.visible()
it.cvImageBg.gone()
it.btnStartPreview.setOnClickListener { _ ->
it.cvImageBg.gone()
it.flCameraPreview.visible()
}
it.btnTakePhoto.setOnClickListener { _ ->
cameraUtils.takePhoto(takePhotoCallback)
}
}
return
}
//只显示原始图片
imagePreviewBinding = LayoutImagePreviewBinding.inflate(inflater, binding.flContainer)
imagePreviewBinding?.imageView?.load(goods.relativeUrl)
}
private var box: Box<Food>? = null
private fun collectGoodsVector() {
try {
ImageUtil.uriToBitmap(context, imageUri!!)?.let { bitmap ->
val cropBitmap = BitmapCropper.cropCenter(
original = bitmap,
targetWidth = 1000, targetHeight = 1300,
offsetX = 30, offsetY = 100
)
val file = BitmapSaver.saveToAppFilesDir(
cropBitmap, context, "IMG_CROP_${System.currentTimeMillis()}.jpg"
)
Timber.d("${this.javaClass.simpleName}-collectGoodsVector-裁剪bitmap保存文件路径:${file?.absolutePath}")
if (box == null) {
box = ObjectBox.boxStore.boxFor(Food::class)
}
Thread {
try {
val imageVector = FoodModule.bitmap2FloatArray(bitmap)
box?.put(Food(name = goods.goodsName, foodIdx = 0, foodVector = imageVector))
} catch (e: Exception) {
e.printStackTrace()
}
}.start()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
} }
@@ -96,7 +96,7 @@ object FoodModule {
nameScoreList.add(IdNameScore(id = it.id, name = box.get(it.id).name?:"", score = it.score)) nameScoreList.add(IdNameScore(id = it.id, name = box.get(it.id).name?:"", score = it.score))
} }
Timber.tag("FoodModule").d("queryFood数据:${nameScoreList.toJsonString()}") Timber.tag("FoodModule").d("queryFood数据:${nameScoreList.toJsonString()}")
idScoreList.filter { it.score < 0.07 }.forEach { idScoreList.filter { it.score < 0.05 }.forEach {
val food = box.get(it.id) val food = box.get(it.id)
//FoodQueryResult(id = it.id, name = food.name, foodIdx = food.foodIdx, score = it.score) //FoodQueryResult(id = it.id, name = food.name, foodIdx = food.foodIdx, score = it.score)
food.name?.let { key -> food.name?.let { key ->
@@ -206,8 +206,8 @@ class CrashHandler private constructor(private val context: Context) :
// val crashDir = File(context.getExternalFilesDir(null), CRASH_REPORTS_DIR) // val crashDir = File(context.getExternalFilesDir(null), CRASH_REPORTS_DIR)
val crashDir = File(context.filesDir, CRASH_REPORTS_DIR) val crashDir = File(context.filesDir, CRASH_REPORTS_DIR)
if (!crashDir.exists() && !crashDir.mkdirs()) { if (!crashDir.exists()) {
Timber.tag(TAG).e("Failed to create crash report directory") crashDir.mkdirs()
return return
} }
@@ -302,15 +302,18 @@
<FrameLayout <FrameLayout
android:id="@+id/flContainer" android:id="@+id/flContainer"
android:layout_width="488dp" android:layout_width="match_parent"
android:layout_height="356dp" android:layout_height="wrap_content"
android:background="@drawable/bg_white_radius10_stroke2" android:layout_marginBottom="15dp"/>
android:padding="2dp"/> <!-- android:padding="2dp"-->
<!-- android:layout_width="488dp"-->
<!-- android:layout_height="356dp"-->
<!-- android:layout_marginBottom="37dp"-->
<!-- android:background="@drawable/bg_white_radius10_stroke2"-->
<FrameLayout <FrameLayout
android:layout_width="488dp" android:layout_width="488dp"
android:layout_height="70dp" android:layout_height="70dp"
android:layout_marginTop="37dp"
android:background="@drawable/bg_gray_radius10"> android:background="@drawable/bg_gray_radius10">
<TextView <TextView
@@ -17,7 +17,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="50dp" android:layout_marginTop="50dp"
android:text="系统异常,请稍后重试!" android:text="识别失败,请稍后重试!"
android:textColor="#ff000000" android:textColor="#ff000000"
android:textSize="36sp" android:textSize="36sp"
android:textStyle="bold" /> android:textStyle="bold" />
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:layout_gravity="center"
tools:ignore="ContentDescription"
tools:src="@mipmap/ic_launcher" />
</merge>
@@ -0,0 +1,137 @@
<?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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<FrameLayout
android:layout_width="488dp"
android:layout_height="325dp"
android:background="@drawable/bg_white_radius10_stroke2_dash">
<androidx.cardview.widget.CardView
android:id="@+id/flCameraPreview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
app:cardCornerRadius="10dp"
app:cardElevation="0dp">
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/cvImageBg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="3dp"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="10dp"
app:cardElevation="0dp">
<ImageView
android:id="@+id/ivGoodsImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:scaleType="centerCrop"
tools:ignore="ContentDescription" />
</androidx.cardview.widget.CardView>
<LinearLayout
android:id="@+id/llCameraFlag"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="@drawable/bg_white_radius10_stroke2_dash"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="96dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleType="centerCrop"
android:src="@mipmap/ic_camera_gray"
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="24sp"
tools:ignore="HardcodedText" />
</LinearLayout>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<Button
android:id="@+id/btnStartPreview"
android:layout_width="150dp"
android:layout_height="50dp"
android:background="@drawable/bg_white_stroke_green_ripple"
android:text="取消"
android:textColor="#FF14CD71"
android:textSize="21sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<LinearLayout
android:id="@+id/btnTakePhoto"
android:layout_width="150dp"
android:layout_height="50dp"
android:background="@drawable/bg_green_ripple"
android:gravity="center">
<ImageView
android:layout_width="26dp"
android:layout_height="wrap_content"
android:src="@mipmap/ic_camera_white"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginStart="10dp"
android:text="采集"
android:textColor="@color/white"
android:textSize="21sp"
android:textStyle="bold"
tools:ignore="HardcodedText,UseCompatTextViewDrawableXml" />
</LinearLayout>
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>