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

Fragment中RecyclerView数据不更新、按钮点击无响应问题求助

问题描述

我是Kotlin新手,接触编程已有几年,正在开发一款卡路里计数器应用,但在添加食物及卡路里数据环节受阻。推测是代码语法错误或对Kotlin逻辑理解不足导致问题。

应用可正常运行,但点击“添加”按钮后,数据无法传递到RecyclerView的TextView中,EditText组件也无法清空,且HomeFragment.kt的onViewCreated方法中按钮的setOnClickListener事件未生效。


相关代码

MainActivity.kt(底部导航栏实现)

package com.example.bbar

import android.os.Bundle
import com.google.android.material.bottomnavigation.BottomNavigationView
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import com.example.bbar.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val navView: BottomNavigationView = binding.navView

        val navController = findNavController(R.id.nav_host_fragment_activity_main)
        // Passing each menu ID as a set of Ids because each
        // menu should be considered as top level destinations.
        val appBarConfiguration = AppBarConfiguration(
            setOf(
                R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications
            )
        )
        setupActionBarWithNavController(navController, appBarConfiguration)
        navView.setupWithNavController(navController)
    }
}

HomeFragment.kt

package com.example.bbar.ui.home

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.bbar.R

import com.example.bbar.databinding.FragmentHomeBinding

class HomeFragment : Fragment() {

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

    private lateinit var foodlistadapter: Foodlistadapter

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentHomeBinding.inflate(inflater, container, false)

        return inflater.inflate(R.layout.fragment_home, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        foodlistadapter = Foodlistadapter(mutableListOf())
        binding.rvFoodItems.adapter = foodlistadapter
        binding.rvFoodItems.layoutManager = LinearLayoutManager(activity)

        binding.bSubmit.setOnClickListener{
            val foodTitle= binding.etName.text.toString()
            if (foodTitle.isNotEmpty()){
                val food= food(foodTitle, binding.etCalories.text.toString().toInt(), false )
                foodlistadapter.addFood(food)

                binding.etName.text.clear()
                binding.etCalories.text.clear()
            }
        }
        binding.bDelete.setOnClickListener{
            foodlistadapter.deleteFood()
        }
    }

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

foodlistadapter.kt

package com.example.bbar.ui.home

import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.bbar.R
import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
import android.widget.TextView
import com.example.bbar.databinding.FooditemBinding

@SuppressLint("StaticFieldLeak")
private var _binding: FooditemBinding? = null
private val binding get() =  _binding!!

class Foodlistadapter(

    private val foods: MutableList<food>
) : RecyclerView.Adapter<Foodlistadapter.foodlistviewholder>() {

    class foodlistviewholder(itemView: View) : RecyclerView.ViewHolder(itemView)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): foodlistviewholder {
        return foodlistviewholder(
            LayoutInflater.from(parent.context).inflate(
                R.layout.fragment_home,
                parent,
                false
            )
        )
    }

     fun addFood(food: food) {
        foods.add(food)
        notifyItemInserted(foods.size - 1)
    }

     fun deleteFood() {
        foods.removeAll { foods -> foods.isChecked }
        notifyDataSetChanged()
    }

    private fun toggleStrikeThrough(tvFood: TextView, isChecked: Boolean) {
        if (isChecked) {
            tvFood.paintFlags = tvFood.paintFlags or STRIKE_THRU_TEXT_FLAG
        } else {
            tvFood.paintFlags = tvFood.paintFlags or STRIKE_THRU_TEXT_FLAG.inv()
        }
    }

    override fun onBindViewHolder(holder: foodlistviewholder, position: Int) {
        val currentFood = foods[position]
        holder.itemView.apply {
            binding.tvFood.text = currentFood.name
            binding.tvCalories.text= currentFood.calories.toString()
            binding.cbDelete.isChecked = currentFood.isChecked
            toggleStrikeThrough(binding.tvFood, currentFood.isChecked)
            binding.cbDelete.setOnCheckedChangeListener { _, isChecked ->
                toggleStrikeThrough(binding.tvFood, isChecked)
                currentFood.isChecked = !currentFood.isChecked
            }
        }
    }

    override fun getItemCount(): Int {
        return foods.size
    }
}

food.kt

package com.example.bbar.ui.home

data class food (
    val name : String,
    val calories: Int,
    var isChecked: Boolean,
    )

错误分析与修复方案

1. HomeFragment视图绑定错误(核心问题)

错误点:onCreateView中初始化了_binding,但返回的是新inflate的R.layout.fragment_home布局,导致binding关联的视图从未被显示在界面上。后续通过binding操作按钮、EditText等控件都是在操作一个“无效”的视图,自然点击事件不生效、EditText无法清空。

修复代码:

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    _binding = FragmentHomeBinding.inflate(inflater, container, false)
    // 返回binding关联的根视图,而不是重新inflate新布局
    return binding.root
}

2. Adapter加载错误布局

错误点:Foodlistadapter的onCreateViewHolder中加载的是R.layout.fragment_home(Fragment的布局),而不是食物项的布局R.layout.fooditem,导致RecyclerView的每个item都是完整的Fragment布局,无法正确显示食物数据。

修复代码:

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): foodlistviewholder {
    return foodlistviewholder(
        LayoutInflater.from(parent.context).inflate(
            R.layout.fooditem, // 替换为食物项的布局ID
            parent,
            false
        )
    )
}

3. Adapter使用全局静态Binding(内存泄漏+显示错乱)

错误点:将_binding声明为全局静态变量,会持有Context引用导致内存泄漏,且所有item会复用同一个Binding实例,造成数据显示错乱。每个RecyclerView的item应该拥有独立的Binding实例。

修复代码:
去掉全局_binding,改为在ViewHolder中绑定FooditemBinding:

class Foodlistadapter(
    private val foods: MutableList<food>
) : RecyclerView.Adapter<Foodlistadapter.FoodListViewHolder>() {

    // 内部ViewHolder持有自己的Binding实例
    class FoodListViewHolder(private val binding: FooditemBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(food: food) {
            binding.tvFood.text = food.name
            binding.tvCalories.text = food.calories.toString()
            binding.cbDelete.isChecked = food.isChecked
            toggleStrikeThrough(binding.tvFood, food.isChecked)
            
            binding.cbDelete.setOnCheckedChangeListener { _, isChecked ->
                toggleStrikeThrough(binding.tvFood, isChecked)
                food.isChecked = isChecked
            }
        }

        private fun toggleStrikeThrough(tvFood: TextView, isChecked: Boolean) {
            if (isChecked) {
                tvFood.paintFlags = tvFood.paintFlags or STRIKE_THRU_TEXT_FLAG
            } else {
                tvFood.paintFlags = tvFood.paintFlags and STRIKE_THRU_TEXT_FLAG.inv()
            }
        }
    }

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

    // ... 其他方法保持不变,仅修改onBindViewHolder
    override fun onBindViewHolder(holder: FoodListViewHolder, position: Int) {
        holder.bind(foods[position])
    }
}

4. deleteFood方法参数命名冲突

错误点:foods.removeAll { foods -> foods.isChecked }中,lambda参数名foods和列表名重复,导致作用域混淆,无法正确引用列表元素。

修复代码:

fun deleteFood() {
    foods.removeAll { food -> food.isChecked } // 参数名改为food,避免和列表名冲突
    notifyDataSetChanged()
}

额外规范建议

遵循Kotlin命名规范:类名首字母大写,将food改为Food,Foodlistadapter改为FoodListAdapter,foodlistviewholder改为FoodListViewHolder,提升代码可读性。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 13:55:00