Refresh Fragment view when RecyclerView.Adapter is changed

Viewed 880

I am following Shrine example application which is implemented in Kotlin part of Google official codelabs for developers. The application and codelab

The last step, We are asked to

Modify your Shrine application to change the product images shown when a category is selected from the backdrop menu.

In the ProductGridFragment#onCreateView I attached a onClick listener on one button to begin with (list of categories, are buttons).

As a test, I just popped an element from the list firstly used in Adapter.

theList = ProductEntry.initProductEntryList(resources)
adapter = StaggeredProductCardRecyclerViewAdapter( theList )

val featured = view.findViewById(R.id.featured) as Button
// set on-click listener
featured.setOnClickListener {
        theList.pop(1)
        adapter.notifyDataSetChanged()

It is not working. Is there other things to do to refresh the view ? all I see in onCreateView seems to be just for first creation of the fragment, I mean, when onClick is produced, it seems to me sufficient to just modify the list and notify adapter on changes.

Do I have to do any thing with view object ?

Any hints ?

Edit 1:

I assigned a new adapter and it works now, this is very nice; Although I do not feel it is performance efficient, as notify for data changes is not really used as intended

featured.setOnClickListener {
    theList = ProductEntry.initProductEntryList(resources, "featured")
    this.adapter = StaggeredProductCardRecyclerViewAdapter(
            theList )
    view.recycler_view.adapter = this.adapter
    this.adapter.notifyDataSetChanged()
}
2 Answers

in Java/Kotlin you always pass by value, not a reference. So when you passed the list to the StaggeredProductCardRecyclerViewAdapter( theList ), you should update the list from Adapter, not the one you passed, so for example:

class StaggeredProductCardRecyclerViewAdapter(private val theList: List) {

    private var listOfItems = theList

    fun removeItem(position: Int) {
        listOfItems = listOfItems.remove(position)
        notifyDatasetChanged()
    }
}

val featured = view.findViewById(R.id.featured) as Button
// set on-click listener
featured.setOnClickListener {
       adapter.removeItem(1)
}

EDIT:

I went to the codebase and you are operating on the immutable list inside the StaggeredProductCardRecyclerViewAdapter. If you want to filter the items on the list based on "featured" for example, you should do this:

class StaggeredProductCardRecyclerViewAdapter(private val initList: List<ProductEntry>?) : RecyclerView.Adapter<StaggeredProductCardViewHolder>() {

    private var productList: List<ProductEntry>? = initList

    override fun getItemViewType(position: Int): Int {
        return position % 3
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StaggeredProductCardViewHolder {
        var layoutId = R.layout.shr_staggered_product_card_first
        if (viewType == 1) {
            layoutId = R.layout.shr_staggered_product_card_second
        } else if (viewType == 2) {
            layoutId = R.layout.shr_staggered_product_card_third
        }

        val layoutView = LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
        return StaggeredProductCardViewHolder(layoutView)
    }

    override fun onBindViewHolder(holder: StaggeredProductCardViewHolder, position: Int) {
        if (productList != null && position < productList.size) {
            val product = productList[position]
            holder.productTitle.text = product.title
            holder.productPrice.text = product.price
            ImageRequester.setImageFromUrl(holder.productImage, product.url)
        }
    }

    override fun getItemCount(): Int {
        return productList?.size ?: 0
    }

    fun replaceList(items: List<ProductEntry>?) {
        productList = items
        notifyDatasetChanged()
    }
}

And in ProductGridFragment:

...
featured.setOnClickListener {
       adapter.replaceList(ProductEntry.initProductEntryList(resources, "featured"))
}

If You want to run again onCreateView with out creating a new instance of the fragment you can just use this:

    supportFragmentManager.beginTransaction().detach(yourFragment).commitNow()
    supportFragmentManager.beginTransaction().attach(yourFragment).commitNow()

This will rerender the whole view.

Based on different cases maybe you need to use childFragmentManager

But the right and most efficient solution when adapter data changes is to use notifyDatasetChanged()

Related