From 0648f458a7698367dff0a3cf5a3332f1b99aafb0 Mon Sep 17 00:00:00 2001 From: lvmeng <848755140@qq.com> Date: Thu, 7 May 2026 18:13:17 +0800 Subject: [PATCH] =?UTF-8?q?refactor(fragment):=20DbInspectFragment=20?= =?UTF-8?q?=E6=94=B9=E7=94=A8=E5=B5=8C=E5=A5=97=20RecyclerView=20=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E5=AD=97=E6=AE=B5=EF=BC=8C=E4=BC=98=E5=8C=96=E5=B7=B2?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E8=AE=B0=E5=BD=95=E8=A7=86=E8=A7=89=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/shuwei/dish/match/db/DbViewModel.kt | 79 ++++++++------ .../match/ui/fragment/DbInspectFragment.kt | 101 ++++++++++-------- .../main/res/layout/list_item_db_field.xml | 9 ++ .../main/res/layout/list_item_db_record.xml | 28 +++-- 4 files changed, 138 insertions(+), 79 deletions(-) create mode 100644 app/src/main/res/layout/list_item_db_field.xml diff --git a/app/src/main/java/com/shuwei/dish/match/db/DbViewModel.kt b/app/src/main/java/com/shuwei/dish/match/db/DbViewModel.kt index a1aac88..42a0151 100644 --- a/app/src/main/java/com/shuwei/dish/match/db/DbViewModel.kt +++ b/app/src/main/java/com/shuwei/dish/match/db/DbViewModel.kt @@ -244,15 +244,27 @@ class DbViewModel : ViewModel() { const val DB_INSPECT_PAGE_SIZE = 50 } + /** + * 单条数据库记录的展示模型 + * @param isDel 是否已逻辑删除 + * @param fields 字段名-值对列表,按顺序排列 + * @param columnsPerRow 每行显示的列数,默认 3;槽位表传 1 保持单列 + */ + data class DbRecord( + val isDel: Boolean, + val fields: List>, + val columnsPerRow: Int = 3 + ) + /** * 数据库调试页面 UI 状态 - * @param items 当前已加载的记录列表,Pair.first=是否逻辑删除,Pair.second=格式化文本 + * @param items 当前已加载的记录列表 * @param totalCount 当前过滤条件下的总记录数 * @param hasMore 是否还有更多数据可加载 * @param isLoading 是否正在加载(首次加载或加载更多) */ data class DbInspectUiState( - val items: List> = emptyList(), + val items: List = emptyList(), val totalCount: Int = 0, val hasMore: Boolean = false, val isLoading: Boolean = false @@ -303,22 +315,25 @@ class DbViewModel : ViewModel() { } /** - * 执行分页查询,返回格式化后的列表和总记录数 + * 执行分页查询,返回 DbRecord 列表和总记录数 */ private suspend fun queryPage( tableIndex: Int, showDel: Boolean, offset: Int - ): Pair>, Int> { + ): Pair, Int> { val limit = DB_INSPECT_PAGE_SIZE return when (tableIndex) { 0 -> { val total = rep.countCookFoodAll(showDel) val rows = rep.getCookFoodPaged(showDel, limit, offset).map { e -> - (e.isDel == 1) to fmt( - "id" to e.id, "foodId" to e.foodId, "foodName" to e.foodName, - "canteenId" to e.canteenId, "foodWeight" to e.foodWeight, "cookMode" to e.cookMode, - "dinnerType" to e.dinnerType, "isDel" to e.isDel, "createTime" to e.createTime + DbRecord( + isDel = e.isDel == 1, + fields = listOf( + "id" to "${e.id}", "foodId" to "${e.foodId}", "foodName" to "${e.foodName}", + "canteenId" to "${e.canteenId}", "foodWeight" to "${e.foodWeight}", "cookMode" to "${e.cookMode}", + "dinnerType" to "${e.dinnerType}", "isDel" to "${e.isDel}", "createTime" to "${e.createTime}" + ) ) } rows to total @@ -326,12 +341,15 @@ class DbViewModel : ViewModel() { 1 -> { val total = rep.countCookFoodGoodsAll(showDel) val rows = rep.getCookFoodGoodsPaged(showDel, limit, offset).map { e -> - (e.isDel == 1) to fmt( - "id" to e.id, "pid" to e.pid, "goodsId" to e.goodsId, - "goodsName" to e.goodsName, "foodId" to e.foodId, "materialType" to e.materialType, - "relateionType" to e.relateionType, "allEdible" to e.allEdible, "useWeight" to e.useWeight, - "sort" to e.sort, "zjmCode" to e.zjmCode, "materId" to e.materId, - "isDel" to e.isDel, "createTime" to e.createTime + DbRecord( + isDel = e.isDel == 1, + fields = listOf( + "id" to "${e.id}", "pid" to "${e.pid}", "goodsId" to "${e.goodsId}", + "goodsName" to "${e.goodsName}", "foodId" to "${e.foodId}", "materialType" to "${e.materialType}", + "relateionType" to "${e.relateionType}", "allEdible" to "${e.allEdible}", "useWeight" to "${e.useWeight}", + "sort" to "${e.sort}", "zjmCode" to "${e.zjmCode}", "materId" to "${e.materId}", + "isDel" to "${e.isDel}", "createTime" to "${e.createTime}" + ) ) } rows to total @@ -339,11 +357,14 @@ class DbViewModel : ViewModel() { 2 -> { val total = rep.countSeasoningAll(showDel) val rows = rep.getSeasoningPaged(showDel, limit, offset).map { e -> - (e.isDel == 1) to fmt( - "id" to e.id, "goodsId" to e.goodsId, "goodsName" to e.goodsName, - "sort" to e.sort, "materialType" to e.materialType, "useWeight" to e.useWeight, - "zjmCode" to e.zjmCode, "materId" to e.materId, "isDel" to e.isDel, - "createTime" to e.createTime + DbRecord( + isDel = e.isDel == 1, + fields = listOf( + "id" to "${e.id}", "goodsId" to "${e.goodsId}", "goodsName" to "${e.goodsName}", + "sort" to "${e.sort}", "materialType" to "${e.materialType}", "useWeight" to "${e.useWeight}", + "zjmCode" to "${e.zjmCode}", "materId" to "${e.materId}", "isDel" to "${e.isDel}", + "createTime" to "${e.createTime}" + ) ) } rows to total @@ -351,23 +372,21 @@ class DbViewModel : ViewModel() { 3 -> { val total = rep.countSlotAll() val rows = rep.getSlotPaged(limit, offset).map { e -> - false to "deviceId: ${e.deviceId}\naddress: ${e.address}\ngoodsId: ${e.goodsId}\ngoodsName: ${e.goodsName}" + DbRecord( + isDel = false, + fields = listOf( + "deviceId" to e.deviceId, "address" to "${e.address}", + "goodsId" to e.goodsId, "goodsName" to e.goodsName + ), + columnsPerRow = 1 // 槽位表字段少,保持单列显示 + ) } rows to total } - else -> emptyList>() to 0 + else -> emptyList() to 0 } } - /** - * 将字段列表格式化为每行 3 个的紧凑布局 - * 格式:key: value | key: value | key: value - */ - private fun fmt(vararg fields: Pair): String = - fields.toList().chunked(3).joinToString("\n") { row -> - row.joinToString(" | ") { (k, v) -> "$k: ${v ?: "null"}" } - } - //-------------------------------------------------------------------------------------------------------- // viewModelScope.launch { diff --git a/app/src/main/java/com/shuwei/dish/match/ui/fragment/DbInspectFragment.kt b/app/src/main/java/com/shuwei/dish/match/ui/fragment/DbInspectFragment.kt index ce79029..53d89e8 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/fragment/DbInspectFragment.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/fragment/DbInspectFragment.kt @@ -7,7 +7,9 @@ import android.view.View import android.view.ViewGroup import android.widget.RadioButton import android.widget.TextView +import androidx.cardview.widget.CardView import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.shuwei.dish.match.R @@ -22,8 +24,8 @@ import kotlinx.coroutines.launch /** * 数据库调试查看页面 * 支持按表切换、isDel 过滤开关、分页加载(每页 50 条) + * 菜品/食材/调料表每行 3 列等宽显示字段,槽位表保持单列 * 已逻辑删除(isDel=1)的记录以灰色文字 + 半透明卡片区分 - * 槽位表无 isDel 字段,切换到该 Tab 时自动隐藏 isDel 控件 */ class DbInspectFragment : BaseFragment() { @@ -35,10 +37,7 @@ class DbInspectFragment : BaseFragment() { /** 是否显示已删除记录(槽位表忽略此参数) */ private var showDel = true - /** - * 防止在代码中设置 Switch 状态时触发监听器, - * 例如切换回非槽位 Tab 时恢复 Switch 状态 - */ + /** 防止代码中设置 Switch 状态时误触发监听器 */ private var ignoreSwitch = false private val adapter = DbRecordAdapter() @@ -83,7 +82,7 @@ class DbInspectFragment : BaseFragment() { loadFirstPage() } - // 刷新按钮:重新加载第一页 + // 刷新按钮 binding.tvRefresh.setOnClickListener { loadFirstPage() } // 加载更多按钮 @@ -98,30 +97,23 @@ class DbInspectFragment : BaseFragment() { } } - // 初始化 Tab 样式并加载默认表 + // 初始化样式并加载 updateTabStyles() updateDelControlsVisibility() binding.vTabIndicator.post { moveIndicator(currentTableIndex) } loadFirstPage() } - /** 重新从第一页开始加载当前表数据 */ private fun loadFirstPage() { currentActivity.appViewModel.loadDbInspect(currentTableIndex, showDel) } - /** - * 根据 UI 状态更新计数文字、加载更多按钮和列表 - */ private fun renderState(state: DbViewModel.DbInspectUiState) { - // 计数文字 binding.tvCount.text = when { state.isLoading && state.items.isEmpty() -> "加载中..." state.hasMore -> "已加载 ${state.items.size} / 共 ${state.totalCount} 条" else -> "共 ${state.totalCount} 条" } - - // 加载更多按钮:有更多数据时显示,加载中时显示"加载中..."并禁用点击 when { state.isLoading && state.hasMore -> { binding.tvLoadMore.visible() @@ -135,13 +127,11 @@ class DbInspectFragment : BaseFragment() { } else -> binding.tvLoadMore.gone() } - adapter.setData(state.items) } /** - * 槽位表(index=3)没有 isDel 字段,切换到该 Tab 时隐藏 isDel 相关控件; - * 切换回其他 Tab 时恢复,并同步 Switch 状态 + * 槽位表无 isDel 字段,切换到时隐藏相关控件;切换回其他 Tab 时恢复 */ private fun updateDelControlsVisibility() { val isSlot = currentTableIndex == 3 @@ -151,16 +141,12 @@ class DbInspectFragment : BaseFragment() { } else { binding.tvDelLabel.visible() binding.swShowDel.visible() - // 恢复 Switch 显示状态(防止误触发监听器) ignoreSwitch = true binding.swShowDel.isChecked = showDel ignoreSwitch = false } } - /** - * 更新所有 Tab 的文字颜色和加粗状态 - */ private fun updateTabStyles() { tabButtons.forEachIndexed { index, btn -> val selected = index == currentTableIndex @@ -169,20 +155,12 @@ class DbInspectFragment : BaseFragment() { } } - /** - * 将下方指示条平移到选中 Tab 的正下方(150ms 动画) - */ private fun moveIndicator(index: Int) { val btn = tabButtons.getOrNull(index) ?: return val tabWidth = btn.width.toFloat() val indicatorWidth = tabWidth / 2f val indicatorX = btn.x + (tabWidth - indicatorWidth) / 2f - - binding.vTabIndicator.animate() - .translationX(indicatorX) - .setDuration(150) - .start() - + binding.vTabIndicator.animate().translationX(indicatorX).setDuration(150).start() binding.vTabIndicator.post { val lp = binding.vTabIndicator.layoutParams lp.width = indicatorWidth.toInt() @@ -190,14 +168,14 @@ class DbInspectFragment : BaseFragment() { } } - // ---- 内部 Adapter ---- + // ---- 外层 Adapter:每条数据库记录对应一个 CardView ---- - /** 数据库记录列表适配器 */ + /** 外层列表适配器,每个 item 内嵌一个 GridLayoutManager 的 RecyclerView 展示字段 */ private inner class DbRecordAdapter : RecyclerView.Adapter() { - private val data = mutableListOf>() + private val data = mutableListOf() - fun setData(list: List>) { + fun setData(list: List) { data.clear() data.addAll(list) notifyDataSetChanged() @@ -210,20 +188,57 @@ class DbInspectFragment : BaseFragment() { } override fun onBindViewHolder(holder: VH, position: Int) { - val (isDeleted, content) = data[position] - holder.tvContent.text = content - // isDel=1 的记录用灰色标识,正常记录用深色 - holder.tvContent.setTextColor( - if (isDeleted) Color.parseColor("#BBBBBB") else Color.parseColor("#333333") - ) - // 已删除记录卡片略微透明 - holder.itemView.alpha = if (isDeleted) 0.6f else 1.0f + val record = data[position] + + if (record.isDel) { + // 已删除:浅红背景 + 左侧红色标记条 + 文字颜色正常(深色可读) + holder.card.setCardBackgroundColor(Color.parseColor("#FFF3F3")) + holder.vDelMark.visibility = View.VISIBLE + } else { + // 正常记录:白色背景,隐藏标记条 + holder.card.setCardBackgroundColor(Color.WHITE) + holder.vDelMark.visibility = View.GONE + } + val textColor = if (record.isDel) Color.parseColor("#C62828") else Color.parseColor("#333333") + + // 每次绑定时重新设置 GridLayoutManager 列数(槽位表为 1,其余为 3) + holder.rvFields.layoutManager = GridLayoutManager(holder.rvFields.context, record.columnsPerRow) + holder.rvFields.adapter = DbFieldAdapter(record.fields, textColor) } override fun getItemCount() = data.size inner class VH(itemView: View) : RecyclerView.ViewHolder(itemView) { - val tvContent: TextView = itemView.findViewById(R.id.tvContent) + val card: CardView = itemView.findViewById(R.id.cardRecord) + val vDelMark: View = itemView.findViewById(R.id.vDelMark) + val rvFields: RecyclerView = itemView.findViewById(R.id.rvFields) + } + } + + // ---- 内层 Adapter:每个字段对应一个 TextView,垂直显示字段名和值 ---- + + /** 内层字段适配器,每个 item 为单个 TextView,显示格式:字段名\n字段值 */ + private inner class DbFieldAdapter( + private val fields: List>, + private val textColor: Int + ) : RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.list_item_db_field, parent, false) + return VH(view) + } + + override fun onBindViewHolder(holder: VH, position: Int) { + val (key, value) = fields[position] + holder.tvField.text = "$key\n$value" + holder.tvField.setTextColor(textColor) + } + + override fun getItemCount() = fields.size + + inner class VH(itemView: View) : RecyclerView.ViewHolder(itemView) { + val tvField: TextView = itemView.findViewById(R.id.tvField) } } } diff --git a/app/src/main/res/layout/list_item_db_field.xml b/app/src/main/res/layout/list_item_db_field.xml new file mode 100644 index 0000000..f4ebffa --- /dev/null +++ b/app/src/main/res/layout/list_item_db_field.xml @@ -0,0 +1,9 @@ + + diff --git a/app/src/main/res/layout/list_item_db_record.xml b/app/src/main/res/layout/list_item_db_record.xml index 3b5b1dd..3084e1e 100644 --- a/app/src/main/res/layout/list_item_db_record.xml +++ b/app/src/main/res/layout/list_item_db_record.xml @@ -1,6 +1,7 @@ - + android:orientation="horizontal"> + + + + + + + +