'How can I display each item from an array of list in a RecyclerView using paging library
Good morning everyone, I hope you're doing great.
I'm working with Room database with two tables or entities and want to display the list of all courses taken by a student in a recycler View using paging librar. In bind() method of the adapter, when I first used binding.courseName.text = studentCourse.courses.courseName.toString() to see the output after saving four courses in the Database and the name of a student who has taken these courses, it returns an array like this:
**[Course(courseName=Android, courseDuration=50), Course(courseName=Python, courseDuration=40), Course(courseName=Kotlin, courseDuration=36), Course(courseName=English, courseDuration=25)]
With the code below, it returns only one course with its name and duration in the recycler view and that is the last course of the array .
The two tables or entities Course and Student are defined like this:
@Entity(tableName = "course_table")
data class Course(
@PrimaryKey(autoGenerate = false)
val courseName : String,
@ColumnInfo(name = "course_duration")
val courseDuration : String
)
@Entity(tableName = "student_table")
data class Student(
@PrimaryKey(autoGenerate = false)
val studentName : String,
val semester : Int,
val schoolName : String
)
The relations between these two classes are represented by StudentAndCourse and CourseAndStudent** classes defined like:
data class StudentAndCourse(
@Embedded
val student : Student,
@Relation(
parentColumn = "studentName",
entityColumn = "courseName",
associateBy = Junction(StudentAndCourseTogether::class)
)
val courses : List<Course>
)
data class CourseAndStudent(
@Embedded
val course : Course,
@Relation(
parentColumn = "courseName",
entityColumn = "studentName",
associateBy = Junction(StudentAndCourseTogether::class)
)
val students : List<Student>
)
The query I defined in Dao to get all courses taken by a specific student identified by his name is :
@Query("SELECT * FROM student_table WHERE studentName = :studentName")
fun getAllCoursesByStudentName(studentName: String) : PagingSource<Int, StudentAndCourse>```
Here is my Paging Adapter class:
class CourseByStudentNameAdapter : PagingDataAdapter<StudentAndCourse, CourseByStudentNameAdapter.CourseByStudentNameViewHolder>(DiffCallback) {
class CourseByStudentNameViewHolder(private val binding: CourseByStudentNameItemBinding) :
RecyclerView.ViewHolder(binding.root){
fun bind(studentAndCourse: StudentAndCourse){
for (course in studentAndCourse.courses){
binding.courseName.text = course.courseName
binding.courseDuration.text = course.courseDuration
}
}
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int,
): CourseByStudentNameAdapter.CourseByStudentNameViewHolder {
val inflatedLayout = CourseByStudentNameItemBinding.inflate(LayoutInflater.from(
parent.context), parent, false)
return CourseByStudentNameViewHolder(inflatedLayout)
}
override fun onBindViewHolder(holder: CourseByStudentNameAdapter.CourseByStudentNameViewHolder, position: Int) {
val currentCourse = getItem(position)
if (currentCourse != null) {
holder.bind(currentCourse)
}
}
companion object DiffCallback : DiffUtil.ItemCallback<StudentAndCourse>(){
override fun areItemsTheSame(
oldItem: StudentAndCourse,
newItem: StudentAndCourse
): Boolean = oldItem.courses == newItem.courses
override fun areContentsTheSame(
oldItem: StudentAndCourse,
newItem: StudentAndCourse
): Boolean = oldItem == newItem
}
}```
My XML file that holds items:
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/linear_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:layout_marginTop="4dp"
style="@style/itemListTextStyle"
android:background="@drawable/item_layout_background"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<TextView
android:id="@+id/course_name"
android:layout_width="match_parent"
tools:text="Computer"
android:gravity="center"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/course_duration"
android:layout_width="match_parent"
tools:text="15 hours"
android:gravity="center"
android:layout_height="wrap_content"/>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.constraintlayout.widget.ConstraintLayout>```
My fragment code:
```
@AndroidEntryPoint
class AllCourseByStudentNameFragment : Fragment() {
private var _binding : FragmentAllCourseByStudentNameBinding? = null
private val binding get() = _binding!!
private val viewModel : SchoolViewModel by activityViewModels()
private lateinit var adapter : CourseByStudentNameAdapter
private val schoolName = "IFRI"
private val studentName = "Esperant"
private val courseName = "Android"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
// Inflate the layout for this fragment
_binding = FragmentAllCourseByStudentNameBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
adapter = CourseByStudentNameAdapter()
binding.apply {
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(requireContext())
recyclerView.setHasFixedSize(true)
}
viewModel.setName(studentName)
viewModel.courseByStudentName.observe(viewLifecycleOwner){
adapter.submitData(viewLifecycleOwner.lifecycle, it)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}```
I will be very glad to get your help. Thanks in advance.
Solution 1:[1]
The problem would be in your ViewHolder. The ViewHolder represents a single item in your RecyclerView, not all.
The way you implemented it:
class CourseByStudentNameViewHolder(private val binding: CourseByStudentNameItemBinding) :
RecyclerView.ViewHolder(binding.root){
fun bind(studentAndCourse: StudentAndCourse){
for (course in studentAndCourse.courses){
binding.courseName.text = course.courseName
binding.courseDuration.text = course.courseDuration
}
}
}
As you pass the StudentAndCourse item (which is a single item), your adapter will think there is only 1 item to display. Your ViewHolder should be used to bind the data of a single RecyclerView item (Course). The way you implemented it, you loop over all the courses and for every loop you override the courseName and courseDuration again. Therefore, in the end only the values of the last course will be visible as all previous courses are overwritten.
Instead of using your StudentAndCourse in the adapter, try to pass for example a single Course for each course in the StudentAndCourse object, so the adapter knows there is more than 1 item in the list.
class CourseByStudentNameAdapter : PagingDataAdapter<Course, CourseByStudentNameAdapter.CourseByStudentNameViewHolder>(DiffCallback) {
class CourseByStudentNameViewHolder(private val binding: CourseByStudentNameItemBinding) : RecyclerView.ViewHolder(binding.root){
fun bind(course: Course){
binding.courseName.text = course.courseName
binding.courseDuration.text = course.courseDuration
}
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int,
): CourseByStudentNameAdapter.CourseByStudentNameViewHolder {
val inflatedLayout = CourseByStudentNameItemBinding.inflate(LayoutInflater.from(
parent.context), parent, false)
return CourseByStudentNameViewHolder(inflatedLayout)
}
override fun onBindViewHolder(holder: CourseByStudentNameAdapter.CourseByStudentNameViewHolder, position: Int) {
val currentCourse = getItem(position)
if (currentCourse != null) {
holder.bind(currentCourse)
}
}
companion object DiffCallback : DiffUtil.ItemCallback<Course>(){
override fun areItemsTheSame(
oldItem: Course,
newItem: Course
): Boolean = oldItem.courseName == newItem.courseName
override fun areContentsTheSame(
oldItem: Course,
newItem: Course
): Boolean = oldItem == newItem
}
}
You can take a look at the official documentation for paged data: Documentation
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Ilias |
