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.xmlyou definerecipeRowvariable 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 thisbinding.recipeNameButton.text = recipeRow.recipeand 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.recipeand set therecipeRowvariable. This will automatically set the text when recipeRow variable changes.Solution 2
The classic way, remove
android:text="@={recipeRow.recipe}"from yourrecipe_row_view.xmland keepbinding.recipeNameButton.text = recipeRow.recipe.