Home > Back-end >  RecyclerView not displaying List items - FATAL EXCEPTION
RecyclerView not displaying List items - FATAL EXCEPTION

Time:03-02

My List doesn't seem to be getting initialised and I'm not really sure what I should be doing here, everytime I run I get:

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.alarmclockproject, PID: 13493
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
    at com.example.alarmclockproject.fragments.AlarmListAdapter.onBindViewHolder(AlarmListAdapter.kt:27)

Removing the notifyDataSetChanged() stops the fatal crash, but then shows no alarms on fragment, despite multiple being in the database ( checked using DB Browser )

Here is my Adapter:

class AlarmListAdapter() :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {

private var alarmList: List<Alarm> = ArrayList()


class ViewHolder(binding: FragmentHomeBinding) : RecyclerView.ViewHolder(binding.root)


override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    val binding = FragmentHomeBinding.inflate(LayoutInflater.from(parent.context))
    return ViewHolder(binding)
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    val currentItem = alarmList[position]
    holder.itemView.findViewById<TextView>(R.id.tv_alarm_time).text =
        "${currentItem.hour}:${currentItem.minute}"
    //holder.itemView.findViewById<TextView>(R.id.tv_repeat_days)
}

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

fun setData(alarm: List<Alarm>){
    this.alarmList = alarm
    notifyDataSetChanged()
}

}

My HomeFragment where the recycler view is displayed:

class HomeFragment : Fragment() {

lateinit var binding: FragmentHomeBinding
private lateinit var alarmViewModel: AlarmViewModel


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

    // RecyclerView
    val adapter = AlarmListAdapter()
    val recyclerView = binding.recyclerView
    recyclerView.adapter = adapter
    recyclerView.layoutManager = LinearLayoutManager(requireContext())

    //ViewModel
    alarmViewModel = ViewModelProvider(this).get(AlarmViewModel::class.java)
    alarmViewModel.readAlarmData.observe(viewLifecycleOwner, Observer { alarm ->
        adapter.setData(alarm)
    })

    binding.btnAddAlarm.setOnClickListener{
        Navigation.findNavController(requireView()).navigate(R.id.action_homeFragment_to_newAlarmFragment)
    }
    return binding.root
}
}

and it's layout:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@ id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.HomeFragment">

<TextView
    android:id="@ id/tv_next_alarm"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="64dp"
    android:text="11:11"
    android:textAppearance="@style/TextAppearance.AppCompat.Display3"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.498"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<Button
    android:id="@ id/btn_add_alarm"
    android:layout_width="57dp"
    android:layout_height="57dp"
    android:layout_marginBottom="52dp"
    android:background="@drawable/round_button"
    android:text=" "
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent" />

<androidx.recyclerview.widget.RecyclerView
    android:id="@ id/recycler_view"
    android:layout_width="411dp"
    android:layout_height="446dp"
    android:layout_marginStart="1dp"
    android:layout_marginTop="1dp"
    android:layout_marginEnd="1dp"
    android:layout_marginBottom="1dp"
    app:layout_constraintBottom_toTopOf="@ id/btn_add_alarm"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@ id/tv_next_alarm" />

    </androidx.constraintlayout.widget.ConstraintLayout>

ViewModel:

class AlarmViewModel(application: Application) : AndroidViewModel(application) {

val readAlarmData: LiveData<List<Alarm>>
private val repository: AlarmRepository

init {
    val alarmDao = AlarmsDatabase.getDatabase(application).alarmDao()
    repository = AlarmRepository(alarmDao)
    readAlarmData = repository.readAlarmData
}

fun addAlarm(alarm: Alarm) {
    viewModelScope.launch(Dispatchers.IO) {
        repository.addAlarm(alarm)
    }
}
}

Dao:

@Dao
interface AlarmDao {

@Insert()
suspend fun addAlarm(alarm: Alarm)

@Query("SELECT * FROM alarm_table ORDER BY  id ASC")
fun readAlarmData(): LiveData<List<Alarm>>

@Update
fun updateAlarm(alarm: Alarm)
}

and the Alarm Class:

    @Entity(tableName = "alarm_table")
data class Alarm (
@PrimaryKey(autoGenerate = true)
val id: Int,
val hour: Int,
val minute: Int,
val repeat: Boolean
)

CodePudding user response:

The following line in onCreateViewHolder() is causing the crash:

val binding = FragmentHomeBinding.inflate(LayoutInflater.from(parent.context))

You are inflating the FragmentHomeBinding, which has no TextView with id R.id.tv_alarm_time. Use that of the RecyclerView item instead.

  • Related