Home > OS >  List is not updated after notifyDataSetChanged what should I do?
List is not updated after notifyDataSetChanged what should I do?

Time:03-02

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)
})
  • Related