I am fetching JSON data from API and passing it in recycler view but if I want to fetch new data and display it in recycler view then I have to clear the list and then add new data in that list and notify the adapter that the data is changed but it is not updated what should I do?
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var recipeViewModel: RecipeViewModel
private lateinit var mainBinding: ActivityMainBinding
private lateinit var recipeAdapter: RecipeAdapter
private lateinit var recipeItemList: ArrayList<Hit>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(mainBinding.root)
recipeViewModel =
ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory
.getInstance(application)
)[RecipeViewModel::class.java]
recipeItemList = arrayListOf()
mainBinding.recyclerView.layoutManager = LinearLayoutManager(this)
mainBinding.recyclerView.hasFixedSize()
recipeAdapter = RecipeAdapter(this)
mainBinding.recyclerView.adapter = recipeAdapter
recipeViewModel.recipeLiveData.observe(this, Observer { recipeItems ->
recipeItemList.addAll(recipeItems.hits)
recipeAdapter.updateRecipes(recipeItemList)
Log.d("RESPONSE", recipeItems.toString())
Log.d("List size", recipeAdapter.itemCount.toString())
})
searchRecipeName()
}
private fun searchRecipeName() {
mainBinding.searchRecipeFabBtn.setOnClickListener {
val view = layoutInflater.inflate(R.layout.recipe_search_layout, null)
val searchRecipeET = view.findViewById<EditText>(R.id.searchRecipeET)
val searchRecipeBtn = view.findViewById<Button>(R.id.searchRecipeBtn)
val bottomSheetDialog = BottomSheetDialog(this)
bottomSheetDialog.apply {
this.setContentView(view)
this.show()
}
searchRecipeBtn.setOnClickListener {
val recipeName = searchRecipeET.text.toString()
searchRecipeName(recipeName, searchRecipeET, bottomSheetDialog)
}
}
}
private fun searchRecipeName(
recipeName: String,
searchRecipeET: EditText,
bottomSheetDialog: BottomSheetDialog
) {
if (recipeName.isEmpty()) {
searchRecipeET.error = "Please enter recipe name"
} else {
recipeViewModel.getRecipes(recipeName)
bottomSheetDialog.dismiss()
}
}
}
RecipeAdapter.kt
class RecipeAdapter(val context: Context) : RecyclerView.Adapter<RecipeAdapter.RecipeViewHolder>() {
private val recipesList: ArrayList<Hit> = arrayListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecipeViewHolder {
val layoutInflater = LayoutInflater.from(context)
val view = layoutInflater.inflate(R.layout.recipe_items_layout, null, false)
return RecipeViewHolder(view)
}
override fun onBindViewHolder(holder: RecipeViewHolder, position: Int) {
val currentItem = recipesList[position]
holder.recipeImageView.load(currentItem.recipe.image)
holder.recipeNameText.text = currentItem.recipe.label
}
override fun getItemCount(): Int {
return recipesList.size
}
class RecipeViewHolder(itemView: View) :RecyclerView.ViewHolder(itemView) {
val recipeImageView: ImageView = itemView.findViewById(R.id.recipeImageView)
val recipeNameText: TextView = itemView.findViewById(R.id.recipeNameText)
}
fun updateRecipes(newRecipesList: ArrayList<Hit>){
recipesList.clear()
Log.d("RECIPE SIZE", "${recipesList.size}")
recipesList.addAll(newRecipesList)
notifyDataSetChanged()
}
}
CodePudding user response:
This may be helpful.
Be careful of this :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(mainBinding.root)
recipeViewModel =
ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory
.getInstance(application)
)[RecipeViewModel::class.java]
recipeItemList = arrayListOf()
mainBinding.recyclerView.layoutManager = LinearLayoutManager(this)
mainBinding.recyclerView.hasFixedSize()
recipeAdapter = RecipeAdapter(this)
mainBinding.recyclerView.adapter = recipeAdapter
recipeViewModel.recipeLiveData.observe(this, Observer { recipeItems ->
// You're adding items here but never clear the list
// list will be bigger every time you'll be notified
// recipeItemList.addAll(recipeItems.hits
// recipeAdapter.updateRecipes(recipeItemList)
// Do this instead
recipeItemList = recipeItems.hits
recipeAdapter.updateRecipes(recipeItemList)
Log.d("RESPONSE", recipeItems.toString())
Log.d("List size", recipeAdapter.itemCount.toString())
})
searchRecipeName()
}
Also, here: It's a little better to do this (https://stackoverflow.com/a/10298038/4221943)
fun updateRecipes(newRecipesList: ArrayList<Hit>){
recipesList = newRecipesList
Log.d("RECIPE SIZE", "${recipesList.size}")
notifyDataSetChanged()
}
BTW it will always be more efficient to use the more specific change events if you can. Rely on notifyDataSetChanged()
as a last resort. It is also good practice to use notifyItemInserted(mItems.size() - 1)
for "easier" solution.
CodePudding user response:
You could convert the RecyclerView.Adapter
into a ListAdapter
:
class RecipeAdapter(val context: Context) : ListAdapter<Hit, RecipeAdapter.RecipeViewHolder>(RecipeDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecipeViewHolder {
val layoutInflater = LayoutInflater.from(context)
val view = layoutInflater.inflate(R.layout.recipe_items_layout, null, false)
return RecipeViewHolder(view)
}
override fun onBindViewHolder(holder: RecipeViewHolder, position: Int) {
val currentItem = getItem(position)
holder.recipeImageView.load(currentItem.recipe.image)
holder.recipeNameText.text = currentItem.recipe.label
}
class RecipeViewHolder(itemView: View) :RecyclerView.ViewHolder(itemView) {
val recipeImageView: ImageView = itemView.findViewById(R.id.recipeImageView)
val recipeNameText: TextView = itemView.findViewById(R.id.recipeNameText)
}
}
class RecipeDiffCallback : DiffUtil.ItemCallback<Hit>() {
// Change this condition based on the attribute of `Hit` that will change
override fun areItemsTheSame(oldItem: Hit, newItem: Hit): Boolean = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Hit, newItem: Hit): Boolean = oldItem == newItem
}
Then update its content with the submitList
method.
Every item not satisfying the RecipeDiffCallback
conditions will be automatically updated:
recipeViewModel.recipeLiveData.observe(this, Observer { recipeItems ->
recipeAdapter.submitList(recipeItems.hits)
})