You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin RecyclerView分类项编辑更新数据库功能异常求助

核心问题排查

你的更新失败问题本质是数据库无法定位待修改条目:你点击确认时只将新名称传给了上层,没有携带对应分类的唯一主键(一般是id字段),直接新建无主键的Category对象调用更新方法,数据库找不到匹配条目自然不会执行更新操作。
除此之外原有代码还有性能冗余、编辑状态滚动丢失、重复绑定监听等问题,以下是优化后的完整实现:


优化代码实现

1. CategoryAdapter 改造

class CategoryAdapter(
    private var categoryList: MutableList<Category> = mutableListOf(),
    val listener: ClickListener
) : RecyclerView.Adapter<CategoryAdapter.MyViewHolder>() {

    // 记录当前正在编辑的条目位置,解决滚动后编辑状态丢失问题
    private var currentEditPosition = -1

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val binding = CategoryItemBinding.inflate(
            LayoutInflater.from(parent.context), 
            parent, 
            false
        )
        return MyViewHolder(binding)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val item = categoryList[position]
        holder.binding.apply {
            // 根据编辑状态显示对应布局
            if (position == currentEditPosition) {
                categoryName.visibility = View.GONE
                categoryEditName.visibility = View.VISIBLE
                categoryEditName.setText(item.name)
                categoryEditIv.visibility = View.GONE
                categoryAgreeIv.visibility = View.VISIBLE
            } else {
                categoryName.visibility = View.VISIBLE
                categoryEditName.visibility = View.GONE
                categoryEditIv.visibility = View.VISIBLE
                categoryAgreeIv.visibility = View.GONE
                categoryName.text = item.name
            }
        }
    }

    override fun getItemCount() = categoryList.size

    // 对外暴露更新列表的方法,避免重复创建Adapter
    fun updateList(newList: List<Category>) {
        categoryList = newList.toMutableList()
        // 要求不高可以直接用notifyDataSetChanged,性能要求高可替换为DiffUtil实现
        notifyDataSetChanged()
    }

    inner class MyViewHolder(val binding: CategoryItemBinding) : RecyclerView.ViewHolder(binding.root) {
        // 点击事件绑定放到ViewHolder初始化,避免每次复用重复绑定
        init {
            // 编辑按钮点击
            binding.categoryEditIv.setOnClickListener {
                val position = adapterPosition
                if (position == RecyclerView.NO_POSITION) return@setOnClickListener
                // 先取消之前的编辑状态
                val preEditPos = currentEditPosition
                currentEditPosition = position
                if (preEditPos != -1) notifyItemChanged(preEditPos)
                notifyItemChanged(currentEditPosition)
            }

            // 确认按钮点击
            binding.categoryAgreeIv.setOnClickListener {
                val position = adapterPosition
                if (position == RecyclerView.NO_POSITION) return@setOnClickListener
                val newName = binding.categoryEditName.text.toString().trim()
                if (newName.isNotEmpty()) {
                    listener.editAgreeClickItem(categoryList[position], newName)
                }
                currentEditPosition = -1
                notifyItemChanged(position)
            }

            // 删除按钮点击
            binding.categoryDeleteIv.setOnClickListener {
                val position = adapterPosition
                if (position == RecyclerView.NO_POSITION) return@setOnClickListener
                listener.deleteClickItem(categoryList[position])
            }
        }
    }

    interface ClickListener {
        // 修改回调携带原Category对象和新名称
        fun editAgreeClickItem(category: Category, newName: String)
        fun deleteClickItem(category: Category)
    }
}

2. MainFragment 改造

class MainFragment : Fragment(), NewCategory.NewCategoryCreateListener, CategoryAdapter.ClickListener {

    private var _binding: FragmentMainBinding? = null
    private val binding get() = _binding!!

    private lateinit var shoppingListViewModel: ShoppingListViewModel
    private lateinit var categoryAdapter: CategoryAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        shoppingListViewModel = ViewModelProvider(requireActivity())[ShoppingListViewModel::class.java]
    }

    override fun onCreateView(
        inflater: LayoutInflater, 
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentMainBinding.inflate(layoutInflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 初始化Adapter只做一次
        categoryAdapter = CategoryAdapter(listener = this)
        binding.categoryRV.apply {
            layoutManager = LinearLayoutManager(requireContext())
            adapter = categoryAdapter
        }

        // 观察数据变更只更新Adapter内容,不重复创建实例
        shoppingListViewModel.allCategories.observe(viewLifecycleOwner) {
            updateCategories(it)
        }

        binding.addCategory.setOnClickListener {
            val newCategory = NewCategory(this)
            newCategory.show(childFragmentManager, "NewCategory")
        }
    }

    private fun updateCategories(list: List<Category>) {
        if (list.isEmpty()) {
            binding.noResult.visibility = View.VISIBLE
            binding.categoryRV.visibility = View.GONE
        } else {
            binding.noResult.visibility = View.GONE
            binding.categoryRV.visibility = View.VISIBLE
            categoryAdapter.updateList(list)
        }
    }

    override fun newCategoryCreate(text: String) {
        val newCat = Category(text)
        shoppingListViewModel.insertCategory(newCat)
    }

    override fun editAgreeClickItem(category: Category, newName: String) {
        // 修改原有Category的名称,携带原有id传给数据库更新
        category.name = newName
        shoppingListViewModel.updateCategory(category)
    }

    override fun deleteClickItem(category: Category) {
        shoppingListViewModel.deleteCategory(category)
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

补充说明

如果你的Category类的name字段是val不可修改,可以替换编辑回调的逻辑:构造新的Category对象时传入原有id和新name即可,确保数据库能匹配到对应条目。

内容的提问来源于stack exchange,提问作者Matt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 17:06:01