feat(food): 实现食材识别和净材类型功能
- 在FoodAdapter中添加选中状态背景和文字颜色变化 - 修改FoodMaterialAdapter显示食材名称时包含净材类型 - 在FoodModule中解析食材名称中的净材类型信息 - 移除食材识别页面的净材类型选择弹窗流程 - 优化食材识别结果处理逻辑和数据映射 - 添加未识别到食材时的提示对话框 - 实现向量采集页面的搜索功能和实时监听 - 优化网络请求的文件上传媒体类型设置 - 调整食材列表项的布局约束和样式配置
This commit is contained in:
@@ -25,12 +25,14 @@ class FoodAdapter(list: MutableList<GoodsItem>) :
|
|||||||
item: GoodsItem?
|
item: GoodsItem?
|
||||||
) {
|
) {
|
||||||
item ?: return
|
item ?: return
|
||||||
holder.binding.tvGoodsInfo.run {
|
holder.binding.root.run {
|
||||||
text = item.goodsName
|
|
||||||
setBackgroundResource(
|
setBackgroundResource(
|
||||||
if (item.isClicked) R.drawable.shape_green_stroke
|
if (item.isClicked) R.drawable.shape_green_stroke
|
||||||
else R.drawable.shape_white_12_corners
|
else R.drawable.shape_white_12_corners
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
holder.binding.tvGoodsInfo.run {
|
||||||
|
text = item.goodsName
|
||||||
setTextColor(
|
setTextColor(
|
||||||
ContextCompat.getColor(
|
ContextCompat.getColor(
|
||||||
context,
|
context,
|
||||||
@@ -46,6 +48,7 @@ class FoodAdapter(list: MutableList<GoodsItem>) :
|
|||||||
it.visible()
|
it.visible()
|
||||||
val foodScoreText = "${(item.foodScore / 100.0).roundedDecimalPlace(2)}%"
|
val foodScoreText = "${(item.foodScore / 100.0).roundedDecimalPlace(2)}%"
|
||||||
it.text = foodScoreText
|
it.text = foodScoreText
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.rawMaterialsType.isNullOrBlank()) {
|
if (item.rawMaterialsType.isNullOrBlank()) {
|
||||||
|
|||||||
@@ -36,8 +36,11 @@ class FoodMaterialAdapter(list: MutableList<GoodsItem>) :
|
|||||||
clBlock.setOnClickListener {
|
clBlock.setOnClickListener {
|
||||||
onItemClick?.invoke(holder.bindingAdapterPosition)
|
onItemClick?.invoke(holder.bindingAdapterPosition)
|
||||||
}
|
}
|
||||||
|
if (data.isOriginalData) {
|
||||||
tvDishName.text = data.goodsName
|
tvDishName.text = data.goodsName
|
||||||
|
} else {
|
||||||
|
tvDishName.text = data.goodsName + "-${data.rawMaterialsType}"
|
||||||
|
}
|
||||||
tvDishType.text = if (data.materialType == 1) "主辅材:主材" else if (data.materialType == 2) "主辅材:辅材" else ""
|
tvDishType.text = if (data.materialType == 1) "主辅材:主材" else if (data.materialType == 2) "主辅材:辅材" else ""
|
||||||
tvDishWeight.text =
|
tvDishWeight.text =
|
||||||
if (data.useWeight == null || data.useWeight == 0.toDouble()) "" else "${data.useWeight!!.roundedOneDecimalPlace()}克"
|
if (data.useWeight == null || data.useWeight == 0.toDouble()) "" else "${data.useWeight!!.roundedOneDecimalPlace()}克"
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ class NetViewModel(
|
|||||||
params: MutableMap<String, RequestBody>
|
params: MutableMap<String, RequestBody>
|
||||||
) {
|
) {
|
||||||
val fileParts = fileList.map { file ->
|
val fileParts = fileList.map { file ->
|
||||||
file.asRequestBody("multipart/form-data".toMediaTypeOrNull())
|
file.asRequestBody("image/jpeg".toMediaTypeOrNull())
|
||||||
.let { MultipartBody.Part.createFormData("foodPics", file.name, it) }
|
.let { MultipartBody.Part.createFormData("foodPics", file.name, it) }
|
||||||
}
|
}
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@@ -204,6 +204,7 @@ class NetViewModel(
|
|||||||
params: MutableMap<String, RequestBody>
|
params: MutableMap<String, RequestBody>
|
||||||
): UiState<List<String>?> {
|
): UiState<List<String>?> {
|
||||||
val fileParts = fileList.map { file ->
|
val fileParts = fileList.map { file ->
|
||||||
|
//multipart/form-data image/jpeg
|
||||||
file.asRequestBody("multipart/form-data".toMediaTypeOrNull())
|
file.asRequestBody("multipart/form-data".toMediaTypeOrNull())
|
||||||
.let { MultipartBody.Part.createFormData("foodPics", file.name, it) }
|
.let { MultipartBody.Part.createFormData("foodPics", file.name, it) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,7 +129,12 @@ object FoodModule {
|
|||||||
val nameScoreList = mutableListOf<IdNameScore>()
|
val nameScoreList = mutableListOf<IdNameScore>()
|
||||||
idScoreList.forEach {
|
idScoreList.forEach {
|
||||||
val name = foodMap[it.id]?.name ?: ""
|
val name = foodMap[it.id]?.name ?: ""
|
||||||
nameScoreList.add(IdNameScore(id = it.id, name = name, score = it.score, rawMaterialsType = ""))
|
val arr = name.split("&&")
|
||||||
|
var rawMaterialsType = ""
|
||||||
|
if (arr.size > 1) {
|
||||||
|
rawMaterialsType = arr[1]
|
||||||
|
}
|
||||||
|
nameScoreList.add(IdNameScore(id = it.id, name = arr[0], score = it.score, rawMaterialsType = rawMaterialsType))
|
||||||
}
|
}
|
||||||
logInfo("queryFood数据:${nameScoreList.toJsonString()}")
|
logInfo("queryFood数据:${nameScoreList.toJsonString()}")
|
||||||
return nameScoreList
|
return nameScoreList
|
||||||
|
|||||||
@@ -123,6 +123,18 @@ class FoodRecognizeActivity : BaseActivity() {
|
|||||||
|
|
||||||
private val adapter = FoodAdapter(list).apply {
|
private val adapter = FoodAdapter(list).apply {
|
||||||
setOnItemClickListener { _, _, position ->
|
setOnItemClickListener { _, _, position ->
|
||||||
|
// rawMaterialsRemind(position)
|
||||||
|
val item = list[position]
|
||||||
|
list.forEachIndexed { i, it -> it.isClicked = (i == position) }
|
||||||
|
// 点击选择时记录当前秤重量
|
||||||
|
item.useWeight = currentWeight
|
||||||
|
notifyDataSetChanged()
|
||||||
|
selectedPosition = position
|
||||||
|
binding.tvSelectedFood.text = item.goodsName ?: "-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rawMaterialsRemind(position: Int) {
|
||||||
val item = list[position]
|
val item = list[position]
|
||||||
// 弹出净材种类单选弹窗,确认后再选中该食材
|
// 弹出净材种类单选弹窗,确认后再选中该食材
|
||||||
showRawMaterialsDialog(
|
showRawMaterialsDialog(
|
||||||
@@ -134,12 +146,11 @@ class FoodRecognizeActivity : BaseActivity() {
|
|||||||
list.forEachIndexed { i, it -> it.isClicked = (i == position) }
|
list.forEachIndexed { i, it -> it.isClicked = (i == position) }
|
||||||
// 点击选择时记录当前秤重量
|
// 点击选择时记录当前秤重量
|
||||||
item.useWeight = currentWeight
|
item.useWeight = currentWeight
|
||||||
notifyDataSetChanged()
|
adapter.notifyDataSetChanged()
|
||||||
selectedPosition = position
|
selectedPosition = position
|
||||||
binding.tvSelectedFood.text = item.goodsName ?: "-"
|
binding.tvSelectedFood.text = item.goodsName ?: "-"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -178,9 +189,11 @@ class FoodRecognizeActivity : BaseActivity() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 从 Intent 读取数据并初始化页面
|
* 从 Intent 读取数据并初始化页面
|
||||||
|
* Reads data from Intent and initializes the page
|
||||||
*/
|
*/
|
||||||
private fun initData() {
|
private fun initData() {
|
||||||
// 加载食材照片
|
// 加载食材照片
|
||||||
|
// Load food photo
|
||||||
val imageUri = intent.getStringExtra(EXTRA_IMAGE_URI)
|
val imageUri = intent.getStringExtra(EXTRA_IMAGE_URI)
|
||||||
if (!imageUri.isNullOrBlank()) {
|
if (!imageUri.isNullOrBlank()) {
|
||||||
binding.ivFoodPhoto.setImageURI(imageUri.toUri())
|
binding.ivFoodPhoto.setImageURI(imageUri.toUri())
|
||||||
@@ -188,10 +201,14 @@ class FoodRecognizeActivity : BaseActivity() {
|
|||||||
currentWeight = intent.getDoubleExtra(EXTRA_CURRENT_WEIGHT, 0.0)
|
currentWeight = intent.getDoubleExtra(EXTRA_CURRENT_WEIGHT, 0.0)
|
||||||
binding.tvWeight.text = "${currentWeight}g"
|
binding.tvWeight.text = "${currentWeight}g"
|
||||||
// val tempList = arrayListOf<GoodsItem>()
|
// val tempList = arrayListOf<GoodsItem>()
|
||||||
|
// Process recognized name list if available
|
||||||
recognizeNameList?.apply {
|
recognizeNameList?.apply {
|
||||||
|
// Iterate through each recognized item
|
||||||
forEach {
|
forEach {
|
||||||
|
// Extract goods name by splitting "WP" and taking the first part
|
||||||
val goodsName = it.name.split("WP").first()
|
val goodsName = it.name.split("WP").first()
|
||||||
val rawMaterialsType = it.rawMaterialsType
|
val rawMaterialsType = it.rawMaterialsType
|
||||||
|
// Add to goods name list and name-score mapping
|
||||||
goodsNameList.add(GoodsNameQueryDTO(goodsName, rawMaterialsType))
|
goodsNameList.add(GoodsNameQueryDTO(goodsName, rawMaterialsType))
|
||||||
nameScoreMap.put(goodsName, it)
|
nameScoreMap.put(goodsName, it)
|
||||||
|
|
||||||
@@ -203,11 +220,16 @@ class FoodRecognizeActivity : BaseActivity() {
|
|||||||
// // TODO: 测试数据,临时使用-------------------------
|
// // TODO: 测试数据,临时使用-------------------------
|
||||||
// loadRecognizeList(tempList)
|
// loadRecognizeList(tempList)
|
||||||
// // TODO: 测试数据,临时使用-------------------------
|
// // TODO: 测试数据,临时使用-------------------------
|
||||||
|
// Clear the recognize name list after processing
|
||||||
recognizeNameList = null
|
recognizeNameList = null
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: 注释掉等接口正常再调用,临时使用测试数据
|
// If goods name list is not empty, query goods list from network
|
||||||
|
// if (goodsNameList.isNotEmpty()){
|
||||||
netViewModel.queryGoodsList(goodsType = "0", goodsNames = goodsNameList)
|
netViewModel.queryGoodsList(goodsType = "0", goodsNames = goodsNameList)
|
||||||
|
// } else {
|
||||||
|
// loadRecognizeList(emptyList())
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildTestData(item: FoodModule.IdNameScore): GoodsItem {
|
private fun buildTestData(item: FoodModule.IdNameScore): GoodsItem {
|
||||||
@@ -268,11 +290,13 @@ class FoodRecognizeActivity : BaseActivity() {
|
|||||||
binding.tvNoData.gone()
|
binding.tvNoData.gone()
|
||||||
list.clear()
|
list.clear()
|
||||||
goodsList.forEach {
|
goodsList.forEach {
|
||||||
val food = nameScoreMap[it.goodsName]
|
var foodScore = -1
|
||||||
val foodScore = ((1 - (food?.score ?: 0.0)) * 10000).toInt()
|
nameScoreMap[it.goodsName]?.let { food ->
|
||||||
// 将接口返回的 rawMaterialsType(全部种类)移存到 rawMaterialsTypes,供弹窗使用
|
foodScore = ((1 - food.score) * 10000).toInt()
|
||||||
it.rawMaterialsTypes = it.rawMaterialsType
|
}
|
||||||
it.rawMaterialsType = null
|
// // 将接口返回的 rawMaterialsType(全部种类)移存到 rawMaterialsTypes,供弹窗使用
|
||||||
|
// it.rawMaterialsTypes = it.rawMaterialsType
|
||||||
|
// it.rawMaterialsType = null
|
||||||
list.add(it.also { it.foodScore = foodScore })
|
list.add(it.also { it.foodScore = foodScore })
|
||||||
}
|
}
|
||||||
adapter.notifyDataSetChanged()
|
adapter.notifyDataSetChanged()
|
||||||
|
|||||||
@@ -231,6 +231,7 @@ class PrepareFoodActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
!pageVisible || manualCancelFlag || isTakingPhoto.get() || showRecognizePage ||
|
!pageVisible || manualCancelFlag || isTakingPhoto.get() || showRecognizePage ||
|
||||||
|
notRecognizeDialog?.isShowing == true ||
|
||||||
abs(lastWeight - weight) <= WEIGHT_CHANGE_VALUE
|
abs(lastWeight - weight) <= WEIGHT_CHANGE_VALUE
|
||||||
) {
|
) {
|
||||||
Log.d(
|
Log.d(
|
||||||
@@ -574,6 +575,9 @@ class PrepareFoodActivity : BaseActivity() {
|
|||||||
dismissLoading()
|
dismissLoading()
|
||||||
Log.d(TAG, "takePhoto foodScoreList is empty")
|
Log.d(TAG, "takePhoto foodScoreList is empty")
|
||||||
isTakingPhoto.set(false)
|
isTakingPhoto.set(false)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
showNotRecognizeDialog()
|
||||||
|
}
|
||||||
return@withContext
|
return@withContext
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
@@ -586,6 +590,19 @@ class PrepareFoodActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private var notRecognizeDialog: CommonDialog? = null
|
||||||
|
private fun showNotRecognizeDialog() {
|
||||||
|
notRecognizeDialog = CommonDialog(this)
|
||||||
|
.setTitle("提示")
|
||||||
|
.setContent("未识别到食材,请稍后重试")
|
||||||
|
.setNegativeButton("取消")
|
||||||
|
.setPositiveButton("手动设置"){
|
||||||
|
loadRecognizeResultPage(emptyList())
|
||||||
|
}
|
||||||
|
.also {
|
||||||
|
it.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 拍照失败回调
|
* 拍照失败回调
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ import android.view.inputmethod.InputMethodManager
|
|||||||
import androidx.camera.view.PreviewView
|
import androidx.camera.view.PreviewView
|
||||||
import androidx.core.view.doOnLayout
|
import androidx.core.view.doOnLayout
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
|
import androidx.core.widget.addTextChangedListener
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.chad.library.adapter4.util.setOnDebouncedItemClick
|
import com.chad.library.adapter4.util.setOnDebouncedItemClick
|
||||||
@@ -268,7 +271,14 @@ class VectorCollectionFragment : BaseFragment<FragmentVectorCollectionBinding>()
|
|||||||
upload()
|
upload()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.editFoodName.setOnEditorActionListener { v, actionId, event ->
|
binding.editFoodName.let {
|
||||||
|
it.addTextChangedListener { editable ->
|
||||||
|
if (editable?.isEmpty() == true) {
|
||||||
|
searchFood()
|
||||||
|
return@addTextChangedListener
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.setOnEditorActionListener { v, actionId, event ->
|
||||||
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
||||||
searchFood()
|
searchFood()
|
||||||
val imm =
|
val imm =
|
||||||
@@ -279,6 +289,7 @@ class VectorCollectionFragment : BaseFragment<FragmentVectorCollectionBinding>()
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
binding.btnCollectedFood.setOnClickListener {
|
binding.btnCollectedFood.setOnClickListener {
|
||||||
startActivity(Intent(requireActivity(), CollectedFoodActivity::class.java))
|
startActivity(Intent(requireActivity(), CollectedFoodActivity::class.java))
|
||||||
@@ -297,6 +308,22 @@ class VectorCollectionFragment : BaseFragment<FragmentVectorCollectionBinding>()
|
|||||||
it.adapter = searchAdapter
|
it.adapter = searchAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 统一订阅一次,避免每次 searchFood() 重复创建 collect 协程导致多次回调
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
currentActivity.netViewModel.goodsListState.collect { state ->
|
||||||
|
when (state) {
|
||||||
|
is UiState.Success -> {
|
||||||
|
val list = state.data ?: emptyList()
|
||||||
|
if (list.isEmpty()) loadEmptyView() else fillSearchList(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
searchFood()
|
searchFood()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,8 +356,9 @@ class VectorCollectionFragment : BaseFragment<FragmentVectorCollectionBinding>()
|
|||||||
}, onProgress = { count, batch, idList ->
|
}, onProgress = { count, batch, idList ->
|
||||||
showWaitingDialog("图片上传中$count/$totalFileCount")
|
showWaitingDialog("图片上传中$count/$totalFileCount")
|
||||||
val foodList = batch.mapIndexed { index, it ->
|
val foodList = batch.mapIndexed { index, it ->
|
||||||
|
val name = checkedItem!!.goodsName + "&&" + checkedItem!!.rawMaterialsType
|
||||||
Food(
|
Food(
|
||||||
name = checkedItem!!.goodsName,
|
name = name,
|
||||||
foodVector = it.imageVector,
|
foodVector = it.imageVector,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -355,28 +383,11 @@ class VectorCollectionFragment : BaseFragment<FragmentVectorCollectionBinding>()
|
|||||||
fun searchFood() {
|
fun searchFood() {
|
||||||
debouncer.debounce {
|
debouncer.debounce {
|
||||||
val keyword = binding.editFoodName.text.toString().trim()
|
val keyword = binding.editFoodName.text.toString().trim()
|
||||||
// 通过 NetViewModel 查询净材数据(goodsType=0 食材)
|
// 通过 NetViewModel 查询净材数据(goodsType=0 食材),结果由 initialize() 中的 observer 统一处理
|
||||||
currentActivity.netViewModel.queryGoodsList(
|
currentActivity.netViewModel.queryGoodsList(
|
||||||
goodsType = "0",
|
goodsType = "0",
|
||||||
goodsName = keyword.ifEmpty { null }
|
goodsName = keyword.ifEmpty { null }
|
||||||
)
|
)
|
||||||
// 监听查询结果(一次性收集,避免重复订阅)
|
|
||||||
lifecycleScope.launch {
|
|
||||||
currentActivity.netViewModel.goodsListState.collect { state ->
|
|
||||||
when (state) {
|
|
||||||
is UiState.Success -> {
|
|
||||||
val list: List<GoodsItem> = state.data ?: emptyList()
|
|
||||||
if (list.isEmpty()) {
|
|
||||||
// 接口无数据时展示空布局,点击空布局可填充本地假数据用于效果测试
|
|
||||||
loadEmptyView()
|
|
||||||
} else {
|
|
||||||
fillSearchList(list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,6 +411,7 @@ class VectorCollectionFragment : BaseFragment<FragmentVectorCollectionBinding>()
|
|||||||
searchList.clear()
|
searchList.clear()
|
||||||
searchList.addAll(list)
|
searchList.addAll(list)
|
||||||
searchAdapter.notifyDataSetChanged()
|
searchAdapter.notifyDataSetChanged()
|
||||||
|
Log.d(TAG, "fillSearchList,${searchList.toJsonString()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
private var emptyViewBinding: LayoutEmptyViewBinding? = null
|
private var emptyViewBinding: LayoutEmptyViewBinding? = null
|
||||||
|
|||||||
@@ -4,19 +4,23 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="100dp"
|
android:layout_height="100dp"
|
||||||
android:layout_margin="15dp">
|
android:layout_margin="15dp"
|
||||||
|
tools:background="@drawable/shape_white_12_corners">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvGoodsInfo"
|
android:id="@+id/tvGoodsInfo"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/shape_white_12_corners"
|
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:paddingHorizontal="5dp"
|
android:paddingHorizontal="5dp"
|
||||||
android:textColor="@color/black666"
|
android:textColor="@color/black666"
|
||||||
android:textSize="30sp"
|
android:textSize="30sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="椒盐" />
|
tools:text="椒盐" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -29,27 +33,27 @@
|
|||||||
android:textColor="@color/black999"
|
android:textColor="@color/black999"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:layout_marginBottom="5dp"
|
|
||||||
app:layout_constraintHorizontal_chainStyle="packed"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@id/tvFoodScore"
|
app:layout_constraintEnd_toStartOf="@id/tvFoodScore"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/tvGoodsInfo"
|
||||||
tools:text="切丝" />
|
tools:text="切丝" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvFoodScore"
|
android:id="@+id/tvFoodScore"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textColor="@color/black666"
|
android:textColor="@color/black666"
|
||||||
android:layout_marginStart="10dp"
|
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:layout_constraintTop_toTopOf="@id/tvRawMaterialsType"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/tvRawMaterialsType"
|
app:layout_constraintBottom_toBottomOf="@id/tvRawMaterialsType"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@id/tvRawMaterialsType"
|
app:layout_constraintStart_toEndOf="@id/tvRawMaterialsType"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/tvRawMaterialsType"
|
||||||
tools:text="100%" />
|
tools:text="100%" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
Reference in New Issue
Block a user