348 lines
11 KiB
Kotlin
348 lines
11 KiB
Kotlin
package com.sw.inbound.activity
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.graphics.Bitmap
|
|
import android.net.Uri
|
|
import android.os.Bundle
|
|
import android.widget.Toast
|
|
import androidx.activity.ComponentActivity
|
|
import androidx.activity.viewModels
|
|
import androidx.camera.view.PreviewView
|
|
import androidx.core.view.updateLayoutParams
|
|
import androidx.recyclerview.widget.GridLayoutManager
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
import com.sw.inbound.R
|
|
import com.sw.inbound.adapter.FoodCollectionAdapter
|
|
import com.sw.inbound.adapter.GoodsSearchAdapter
|
|
import com.sw.inbound.databinding.ActivityFoodCollectionBinding
|
|
import com.sw.inbound.databinding.LayoutCameraPreviewBinding
|
|
import com.sw.inbound.databinding.LayoutEmptySearchBinding
|
|
import com.sw.inbound.dialog.CollectSearchDialog
|
|
import com.sw.inbound.dialog.Loading
|
|
import com.sw.inbound.model.response.SearchGoodsInfo
|
|
import com.sw.inbound.objbox.Food
|
|
import com.sw.inbound.objbox.FoodCollectionBean
|
|
import com.sw.inbound.objbox.FoodModule
|
|
import com.sw.inbound.objbox.ObjectBox
|
|
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.addOnActionSearchListener
|
|
import com.sw.inbound.utils.ext.clickWithDebounce
|
|
import com.sw.inbound.utils.ext.dp
|
|
import com.sw.inbound.utils.ext.gone
|
|
import com.sw.inbound.utils.ext.hideKeyboard
|
|
import com.sw.inbound.utils.ext.toast
|
|
import com.sw.inbound.utils.ext.visible
|
|
import com.sw.inbound.viewmodel.ReceiptViewModel
|
|
import dagger.hilt.android.AndroidEntryPoint
|
|
import io.objectbox.Box
|
|
import io.objectbox.kotlin.boxFor
|
|
import kotlinx.coroutines.runBlocking
|
|
import timber.log.Timber
|
|
|
|
@SuppressLint("NotifyDataSetChanged")
|
|
@AndroidEntryPoint
|
|
class FoodCollectionActivity : ComponentActivity() {
|
|
|
|
private val TAG = "FoodCollectionActivity"
|
|
|
|
private var box: Box<Food>? = null
|
|
private val collectList: MutableList<FoodCollectionBean> = mutableListOf<FoodCollectionBean>()
|
|
private lateinit var binding: ActivityFoodCollectionBinding
|
|
private lateinit var previewView: PreviewView
|
|
|
|
val viewModel: ReceiptViewModel by viewModels()
|
|
val PAGE_SIZE = 30
|
|
private val cameraUtils: CameraUtils by lazy {
|
|
CameraUtils(this)
|
|
}
|
|
private val collectionAdapter: FoodCollectionAdapter by lazy {
|
|
FoodCollectionAdapter(collectList).apply {
|
|
addOnItemChildClickListener(R.id.ivDelete) { _, _, position ->
|
|
collectList[position].let {
|
|
it.bitmap = null
|
|
it.isFinish = false
|
|
it.isShowCamera = true
|
|
}
|
|
collectionAdapter.notifyItemChanged(position)
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun onResume() {
|
|
super.onResume()
|
|
cameraUtils.bind()
|
|
binding.llCameraFlag.run {
|
|
visible()
|
|
postDelayed({
|
|
gone()
|
|
}, 3000)
|
|
}
|
|
}
|
|
|
|
override fun onPause() {
|
|
super.onPause()
|
|
cameraUtils.unbind()
|
|
binding.llCameraFlag.visible()
|
|
}
|
|
|
|
@SuppressLint("NotifyDataSetChanged")
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
binding = ActivityFoodCollectionBinding.inflate(layoutInflater)
|
|
setContentView(binding.root)
|
|
cameraUtils.initCamera()
|
|
binding.root.setOnClickListener {
|
|
it.hideKeyboard()
|
|
}
|
|
val previewBinding =
|
|
LayoutCameraPreviewBinding.inflate(layoutInflater, binding.flCameraPreview)
|
|
previewView = previewBinding.previewView.also {
|
|
it.updateLayoutParams {
|
|
width = 420.dp
|
|
height = 420.dp
|
|
}
|
|
}
|
|
cameraUtils.setPreviewController(previewView)
|
|
binding.btnBack.setOnClickListener { finish() }
|
|
binding.rvFoodCollection.let {
|
|
it.layoutManager = GridLayoutManager(this, 3, GridLayoutManager.VERTICAL, false)
|
|
it.adapter = collectionAdapter
|
|
}
|
|
|
|
repeat(MAX_COUNT) {
|
|
collectList.add(FoodCollectionBean(isShowCamera = true))
|
|
}
|
|
collectionAdapter.notifyDataSetChanged()
|
|
|
|
binding.ivGoodsSearch.setOnClickListener { searchGoods() }
|
|
binding.etGoodsInput.let { v ->
|
|
v.addOnActionSearchListener {
|
|
searchGoods()
|
|
}
|
|
}
|
|
|
|
binding.btnSave.setOnClickListener {
|
|
if (clickIndex == -1) {
|
|
Toast.makeText(this, "请选择物品名称", Toast.LENGTH_SHORT).show()
|
|
return@setOnClickListener
|
|
}
|
|
val count = collectList.count {it.bitmap!=null}
|
|
if (count == 0) {
|
|
Toast.makeText(this, "请拍摄物品照片", Toast.LENGTH_SHORT).show()
|
|
return@setOnClickListener
|
|
}
|
|
vectorThread()
|
|
}
|
|
binding.btnTakePhoto.clickWithDebounce {
|
|
takePhoto()
|
|
}
|
|
binding.btnCollectedGoods.setOnClickListener {
|
|
CollectSearchDialog(this@FoodCollectionActivity).show()
|
|
}
|
|
binding.btnClearData.setOnClickListener { clearData() }
|
|
binding.rvSearch.let {
|
|
it.layoutManager = GridLayoutManager(this, 2, LinearLayoutManager.VERTICAL, false)
|
|
it.adapter = searchAdapter
|
|
}
|
|
binding.refreshLayout.let {
|
|
it.setEnableRefresh(true)
|
|
it.setEnableLoadMore(false)
|
|
it.setOnRefreshListener {
|
|
pageNo = 1
|
|
searchGoods()
|
|
}
|
|
it.setOnLoadMoreListener {
|
|
searchGoods()
|
|
}
|
|
}
|
|
|
|
loadEmptyView()
|
|
}
|
|
|
|
private var pageNo = 1
|
|
private var clickIndex = -1
|
|
|
|
private val searchGoodsList: MutableList<SearchGoodsInfo.Record> = mutableListOf()
|
|
private val searchAdapter by lazy {
|
|
GoodsSearchAdapter(searchGoodsList).apply {
|
|
isStateViewEnable = true
|
|
setOnItemClickListener { adapter, view, position ->
|
|
clickIndex = position
|
|
if (list[position].isSelected) {
|
|
return@setOnItemClickListener
|
|
}
|
|
list.forEach { item ->
|
|
item.isSelected = false
|
|
}
|
|
list[position].isSelected = true
|
|
notifyDataSetChanged()
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun finishRefresh() {
|
|
binding.refreshLayout.let {
|
|
if (pageNo == 1) {
|
|
it.finishRefresh(500)
|
|
} else {
|
|
it.finishLoadMore(500)
|
|
}
|
|
}
|
|
}
|
|
|
|
val MAX_COUNT = 100
|
|
|
|
@SuppressLint("NotifyDataSetChanged")
|
|
private fun takePhoto() {
|
|
val count = collectList.count { it.bitmap != null }
|
|
if (count == MAX_COUNT) {
|
|
toast("每次只允许保存${MAX_COUNT}条数据")
|
|
return
|
|
}
|
|
cameraUtils.takePhoto(cameraCallback)
|
|
}
|
|
|
|
@SuppressLint("NotifyDataSetChanged")
|
|
private fun clearData() {
|
|
collectList.forEach {
|
|
it.bitmap = null
|
|
it.isShowCamera = true
|
|
it.isFinish = false
|
|
}
|
|
collectionAdapter.notifyDataSetChanged()
|
|
|
|
clickIndex = -1
|
|
binding.etGoodsInput.setText("")
|
|
searchGoodsList.clear()
|
|
searchAdapter.notifyDataSetChanged()
|
|
loadEmptyView()
|
|
}
|
|
|
|
private val cameraCallback:(Uri) -> Unit = { uri->
|
|
val index = collectList.indexOfFirst { it.bitmap == null }
|
|
if (index == -1) {
|
|
toast("每次只允许保存6条数据")
|
|
rerurn@cameraCallback
|
|
}
|
|
ImageUtil.uriToBitmap(this, uri)?.let { bitmap ->
|
|
val cropBitmap = BitmapCropper.cropCenter(
|
|
original = bitmap,
|
|
targetWidth = 1000, targetHeight = 1300,
|
|
offsetX = 30, offsetY = 100
|
|
)
|
|
val file = BitmapSaver.saveToAppFilesDir(
|
|
cropBitmap, this, "IMG_CROP_${System.currentTimeMillis()}.jpg"
|
|
)
|
|
Timber.d("${this.javaClass.simpleName}-searchFood-裁剪bitmap保存文件路径:${file?.absolutePath}")
|
|
|
|
collectList[index].let {
|
|
it.bitmap = cropBitmap
|
|
it.isShowCamera = false
|
|
}
|
|
collectionAdapter.notifyItemChanged(index)
|
|
}
|
|
}
|
|
|
|
private fun vectorThread() {
|
|
Loading.show(this)
|
|
Thread {
|
|
collectList.filter { it.bitmap != null }
|
|
.forEachIndexed { index, it ->
|
|
image2VectorTask(it.bitmap!!, index)
|
|
}
|
|
runOnUiThread {
|
|
window.decorView.postDelayed({
|
|
Loading.dismiss()
|
|
toast("保存成功")
|
|
}, 1000)
|
|
}
|
|
}.start()
|
|
}
|
|
|
|
private fun image2VectorTask(bitmap: Bitmap, position: Int) {
|
|
initBox()
|
|
val imageVector = FoodModule.bitmap2FloatArray(bitmap)
|
|
box?.put(Food(name = searchGoodsList[clickIndex].goodsName, foodIdx = 0, foodVector = imageVector))
|
|
collectList[position].let {
|
|
it.imageVector = imageVector
|
|
it.isFinish = true
|
|
}
|
|
runOnUiThread {
|
|
collectionAdapter.notifyItemChanged(position)
|
|
}
|
|
}
|
|
|
|
@SuppressLint("NotifyDataSetChanged")
|
|
private fun searchGoods() {
|
|
val searchName = binding.etGoodsInput.text.toString().trim()
|
|
if (searchName.isBlank()) {
|
|
toast("请输入物品名称")
|
|
finishRefresh()
|
|
return
|
|
}
|
|
searchGoods(
|
|
searchName = searchName,
|
|
pageNo = pageNo
|
|
) { records ->
|
|
finishRefresh()
|
|
binding.etGoodsInput.hideKeyboard()
|
|
if (pageNo == 1) {
|
|
searchGoodsList.clear()
|
|
}
|
|
searchGoodsList.addAll(records)
|
|
if (searchGoodsList.isEmpty()) {
|
|
loadEmptyView()
|
|
return@searchGoods
|
|
}
|
|
val enableLoadMore = records.size >= PAGE_SIZE
|
|
binding.refreshLayout.setEnableLoadMore(enableLoadMore)
|
|
if (enableLoadMore) {
|
|
pageNo++
|
|
}
|
|
binding.rvSearch.layoutManager =
|
|
GridLayoutManager(this, 2, GridLayoutManager.VERTICAL, false)
|
|
searchAdapter.notifyDataSetChanged()
|
|
}
|
|
}
|
|
|
|
private var emptyBinding: LayoutEmptySearchBinding? = null
|
|
private fun loadEmptyView() {
|
|
binding.rvSearch.layoutManager =
|
|
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
|
if (emptyBinding == null) {
|
|
emptyBinding = LayoutEmptySearchBinding.inflate(layoutInflater, binding.rvSearch, false)
|
|
}
|
|
emptyBinding?.tvContent?.text = "请检查物品名称,或稍后重新搜索"
|
|
emptyBinding?.root?.let { layout ->
|
|
layout.setOnClickListener { layout.hideKeyboard() }
|
|
searchAdapter.stateView = layout
|
|
}
|
|
}
|
|
fun searchGoods(
|
|
searchName: String,
|
|
pageNo: Int,
|
|
pageSize: Int = PAGE_SIZE,
|
|
callback: (List<SearchGoodsInfo.Record>) -> Unit
|
|
) {
|
|
runBlocking {
|
|
val records = viewModel.searchGoodsInfoList2(
|
|
goodsName = searchName,
|
|
pageNo = pageNo,
|
|
pageSize = pageSize
|
|
)
|
|
runOnUiThread {
|
|
callback(records)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun initBox() {
|
|
if (box == null) {
|
|
box = ObjectBox.boxStore.boxFor(Food::class)
|
|
}
|
|
}
|
|
|
|
} |