英文:
Kotlin app crashes after button press with NullPointerException
问题
I am making a Kotlin app in Android Studio where I can add foods with their calorie values like a list.
How it is supposed to work is that the two EditText components, "etName" and "etCalories," would be passed into a list, then the TextView inside the RecyclerView should show them.
The issue is, if I press the button to add the item, the app crashes.
Here's the logcat
FATAL EXCEPTION: main
Process: com.example.bbar, PID: 25930
java.lang.NullPointerException
at com.example.bbar.ui.home.FoodlistadapterKt.getBinding(foodlistadapter.kt:16)
at com.example.bbar.ui.home.FoodlistadapterKt.access$getBinding(foodlistadapter.kt:1)
at com.example.bbar.ui.home.Foodlistadapter.onBindViewHolder(foodlistadapter.kt:57)
at com.example.bbar.ui.home.Foodlistadapter.onBindViewHolder(foodlistadapter.kt:18)
Here's the code
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
}
}
FragmentHome.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 binding.root
}
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()
val foodCalories = binding.etCalories.text.toString()
if (foodTitle.isNotEmpty()) {
val food = food(foodTitle, foodCalories.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
}
}
Summary: Tried to add food with its respected calorie value with a button, but the program crashes.
英文:
I am making a Kotlin app in Android Studio where I can add foods with their calorie values like a list.
How it is supposed to work is that the two EditText components, "etName" and "etCalories", would be passed into a list, then the TextView inside the RecyclerView should show them.
The issue is, if I press the button to add the item, the app crashes.
Here's the logcat
FATAL EXCEPTION: main
Process: com.example.bbar, PID: 25930
java.lang.NullPointerException
at com.example.bbar.ui.home.FoodlistadapterKt.getBinding(foodlistadapter.kt:16)
at com.example.bbar.ui.home.FoodlistadapterKt.access$getBinding(foodlistadapter.kt:1)
at com.example.bbar.ui.home.Foodlistadapter.onBindViewHolder(foodlistadapter.kt:57)
at com.example.bbar.ui.home.Foodlistadapter.onBindViewHolder(foodlistadapter.kt:18)
Here's the code
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
}
}
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 binding!!.root
}
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()
val foodCalories = binding.etCalories.text.toString()
if (foodTitle.isNotEmpty()){
val food= food(foodTitle, foodCalories.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
}
}
Summary: Tried to add food with its respected calorie value with a button, but program crashes.
答案1
得分: 0
你的 binding
为空,而且没有被正确使用。
首先,将声明移到类外部,即:
@SuppressLint("StaticFieldLeak")
private var _binding: FooditemBinding? = null
private val binding get() = _binding!!
将你的 ViewHolder
实现更改为:
class FoodListViewHolder(val binding: FooditemBinding) : RecyclerView.ViewHolder(binding.root)
在 onCreateViewHolder
中实例化该绑定:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FoodListViewHolder {
val inflater = LayoutInflater.from(parent.context)
return FoodListViewHolder(FooditemBinding.inflate(inflater, parent, false))
}
在 onBindViewHolder
中使用绑定:
override fun onBindViewHolder(holder: FoodListViewHolder, position: Int) {
val currentFood = foods[position]
holder.binding.apply {
tvFood.text = currentFood.name
tvCalories.text = currentFood.calories.toString()
cbDelete.isChecked = currentFood.isChecked
toggleStrikeThrough(tvFood, isChecked)
cbDelete.setOnCheckedChangeListener { _, isChecked ->
toggleStrikeThrough(tvFood, isChecked)
currentFood.isChecked = !currentFood.isChecked
}
}
}
英文:
You binding
is null & also it isn't used correctly.
First of, remove the declaration outside the class i.e:
@SuppressLint("StaticFieldLeak")
private var _binding: FooditemBinding? = null
private val binding get() = _binding!!
Change your ViewHolder
implementation to:
class FoodListViewHolder(val binding: FooditemBinding) : RecyclerView.ViewHolder(binding.root)
Inflate that binding in onCreateViewHolder
:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FoodListViewHolder {
val inflater = LayoutInflater.from(parent.context)
return FoodListViewHolder(FooditemBinding.inflate(inflater, parent, false))
}
Use the binding in onBindViewHolder
:
override fun onBindViewHolder(holder: FoodListViewHolder, position: Int) {
val currentFood = foods[position]
holder.binding.apply {
tvFood.text = currentFood.name
tvCalories.text= currentFood.calories.toString()
cbDelete.isChecked = currentFood.isChecked
toggleStrikeThrough(tvFood, isChecked)
cbDelete.setOnCheckedChangeListener { _, isChecked ->
toggleStrikeThrough(tvFood, isChecked)
currentFood.isChecked = !currentFood.isChecked
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论