I set up a RecyclerView
that contains a list of buttons. The length of the list and the text for each button should come from the results of a db query (where I look for all recipes saved in the db).
Somehow, I see the correct text appearing on the buttons for a fraction of a second when I open the fragment, but it disappears immediately after, leaving a list of blank buttons. What am I doing wrong?
Here is the RecipeListAdapter.kt
:
package com.example.recipy
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.recipy.databinding.RecipeRowViewBinding
class RecipeListAdapter(private val recipesList: ArrayList<RecipeRowViewModel>) : RecyclerView.Adapter<RecipeListAdapter.RecipeListViewHolder>() {
inner class RecipeListViewHolder(private val binding : RecipeRowViewBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(recipeRow : RecipeRowViewModel){
binding.recipeNameButton.text = recipeRow.recipe
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecipeListViewHolder =
RecipeListViewHolder(RecipeRowViewBinding.inflate(LayoutInflater.from(parent.context), parent, false))
override fun onBindViewHolder(holder: RecipeListViewHolder, position: Int) {
holder.bind(recipesList[position])
}
override fun getItemCount(): Int {
return recipesList.size
}
}
the RecipeRowViewModel.kt
:
package com.example.recipy
data class RecipeRowViewModel(var recipe : String)
the recipe_row_view.xml
:
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="recipeRow"
type="com.example.recipy.RecipeRowViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/recipe_name_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="@={recipeRow.recipe}">
</Button>
</LinearLayout>
</layout>
the BrowseRecipeFragment
that contains the RecyclerView:
package com.example.recipy
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.recipy.databinding.FragmentBrowseRecipesBinding
class BrowseRecipesFragment : Fragment() {
private var _binding: FragmentBrowseRecipesBinding? = null
private val binding get() = _binding!!
private val recipesList : ArrayList<RecipeRowViewModel> = arrayListOf()
private lateinit var customAdapter : RecipeListAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentBrowseRecipesBinding.inflate(inflater, container, false)
setAdapter()
return binding.root
}
private fun setAdapter(){
customAdapter = RecipeListAdapter(recipesList)
binding.recipeRowRecyclerView.apply {
layoutManager = LinearLayoutManager(requireContext())
adapter = customAdapter
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Retrieve recipes from db and add them to recipesList
val db = DBHelper(requireContext(), null)
val cursor = db.getAllRecipes()
cursor!!.moveToFirst()
recipesList.add(RecipeRowViewModel(cursor.getString(cursor.getColumnIndex(DBHelper.RECIPE_NAME_COL))))
while(cursor.moveToNext()){
recipesList.add(RecipeRowViewModel(cursor.getString(cursor.getColumnIndex(DBHelper.RECIPE_NAME_COL))))
}
cursor.close()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
and fragment_browse_recipes.xml
:
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".BrowseRecipesFragment">
<TextView
android:id="@+id/textview_second"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Here will be the search bar"
android:textSize="20sp"
app:layout_constraintTop_toTopOf="parent" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textview_second" >
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recipe_row_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:listitem="@layout/recipe_row_view" />
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
You have bug in your data binding implementation. In your recipe_row_view.xml
you define recipeRow
variable for the layout but you are not setting value to it. The reason why you see it for short time is that you set the text like this binding.recipeNameButton.text = recipeRow.recipe
and after that data binding is invalidating the layout, but your recipeRow variable is not set, so text is empty.
Solution 1
Using data binding, remove binding.recipeNameButton.text = recipeRow.recipe
and set the recipeRow
variable. This will automatically set the text when recipeRow variable changes.
inner class RecipeListViewHolder(private val binding: RecipeRowViewBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(recipeRow: RecipeRowViewModel) {
//Fix, this will set the layout variable and text gets updated automatically when this changes
binding.recipeRow = recipeRow
}
...
}
Solution 2
The classic way, remove android:text="@={recipeRow.recipe}"
from your recipe_row_view.xml
and keep binding.recipeNameButton.text = recipeRow.recipe
.