2

I have a RecyclerView implemented with the Groupie library and I can delete an item from the list fine, however need to update the view to see the change. I'd like to have something like notifyDataSetChanged() instead, so the list updates immediately. I'm a bit confused at this stage though, tried a few different ways to get an interface from the class that hosts my view holder to be triggered from the fragment that holds the adapter but I think I'm stuck now if I could get some help please.

class RecyclerProductItem(
private val activity: MainActivity,
private val product: Product, private val adapterListener: AdapterListener
) : Item<GroupieViewHolder>() {

companion object {
    var clickListener: AdapterListener? = null
}

override fun bind(viewHolder: GroupieViewHolder, position: Int) {

    viewHolder.apply {

        with(viewHolder.itemView) {

            clickListener = adapterListener

            ivTrash.setOnClickListener(object : View.OnClickListener {
                override fun onClick(v: View?) {
                    if (clickListener != null) {
                        Toast.makeText(context, "delete method to be added here", Toast.LENGTH_SHORT).show()
                        clickListener?.onClickItem(position)
                    } 
                }
            })

        }

    }
}

override fun getLayout() = R.layout.recyclerview_item_row

interface AdapterListener {
    fun onClickItem(position: Int)
}

}

Here it's my fragment. I tried to add a section to the adapter to see if it would allow me to retrieve a listener for it, but as my listener should be triggered under a specific item within the layout, this may not be the best solution, although couldn't make this work either.

class ProductsListFragment : Fragment(), RecyclerProductItem.AdapterListener {

private lateinit var adapter: GroupAdapter<GroupieViewHolder>
private val section = Section()

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    return inflater.inflate(R.layout.fragment_products_list, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    val linearLayoutManager = LinearLayoutManager(activity)
    recyclerView.layoutManager = linearLayoutManager

    adapter = GroupAdapter()

    adapter.add(section)


    recyclerView.adapter = adapter

    loadProducts()

}


private fun loadProducts() {

    GetProductsAPI.postData(object : GetProductsAPI.ThisCallback {

        override fun onSuccess(productList: List<JsonObject>) {

            for (jo in productList) {

                val gson = GsonBuilder().setPrettyPrinting().create()
                val product: Product =
                    gson.fromJson(jo, Product::class.java)

                adapter.add(
                    RecyclerProductItem(
                        activity as MainActivity,
                        Product(
                            product.id,
                            product.title,
                            product.description,
                            product.price
                        ),adapterListenerToBePassedHere 
                    )
                ) // This part is where I should be giving the listener, but get a red line since not sure how to get it to include it here.

            }

        }

    })

}


companion object {
    fun newInstance(): ProductsListFragment {
        return ProductsListFragment()
    }
}


override fun onClickItem(position: Int) {
    
    adapter.notifyItemRemoved(position)
}

}

Many thanks.

Francislainy Campos
  • 3,462
  • 4
  • 33
  • 81
  • Think this is overly complex, because one can call the methods on the adapter. For example: https://stackoverflow.com/questions/26076965/android-recyclerview-addition-removal-of-items – Martin Zeitler Jul 05 '20 at 08:13
  • are you missing getLayout function in viewHolder? and shouldn't you be adding to section you added to adapter and not adapter itself? –  Jul 05 '20 at 08:42
  • Hi, no. I left it without it to remove code that is not directly related to the issue I'm facing so the code here is shortened. I put it back if that can avoid confusion perhaps. – Francislainy Campos Jul 05 '20 at 08:43

2 Answers2

5

I think you are missing this concept from the groupie Readme:

Modifying the contents of the GroupAdapter in any way automatically sends change notifications. Adding an item calls notifyItemAdded(); adding a group calls notifyItemRangeAdded(), etc.

So to remove an item, call section.remove(item). However, in your onClickItem function you currently only pass the position. Pass the item like clickListener?.onClickItem(this@RecyclerProductItem) instead. Even more ideally and safely you should remove by product.id, e.g. clickListener?.onClickItem(this@RecyclerProductItem.product.id) then in onClickItem() you just search for the item with that product id and remove it. Let me know if I'm not clear.

Carson Holzheimer
  • 2,890
  • 25
  • 36
  • 1
    The brilliance of the Groupie library lies in it's functional/declarative approach. You have state in one place (your group adapter) and you don't need to worry about things like `notifyItemRemoved` as it abstracts all that complicated code away for you. – Carson Holzheimer Jul 06 '20 at 05:32
  • Thank you @Carson. Appreciate your reply. I'll need to get back to this later on but will let you know if can make it work with your suggestion. – Francislainy Campos Jul 06 '20 at 05:37
1

Based on @carson's reply, this is what worked for me. Had to add the items to the section, the section to the adapter and then remove the item from the section based on the adapter position once that listener is clicked, passing the method that implements the listener as one of the arguments to complete the GroupAdapter.

class RecyclerProductItem(
private val activity: MainActivity,
private val product: Product, private val adapterListener: AdapterListener
) : Item<GroupieViewHolder>() {

companion object {
    var clickListener: AdapterListener? = null
}

override fun bind(viewHolder: GroupieViewHolder, position: Int) {

    viewHolder.apply {

        with(viewHolder.itemView) {

            tvTitle.text = product.title

            clickListener = adapterListener

            ivTrash.setOnClickListener(object : View.OnClickListener {
                override fun onClick(v: View?) {
                    if (clickListener != null) {
                        Toast.makeText(context, "delete method to be added here", Toast.LENGTH_SHORT).show()
                        clickListener?.onClickItem(this@RecyclerProductItem.product.id, adapterPosition)
                    }
                }
            })

        }

    }
}

override fun getLayout() = R.layout.recyclerview_item_row

interface AdapterListener {
    fun onClickItem(id: Int, position: Int)
}

}

And

private fun loadProducts() {

    GetProductsAPI.postData(object : GetProductsAPI.ThisCallback,
        RecyclerProductItem.AdapterListener {

        override fun onSuccess(productList: List<JsonObject>) {

            Log.i(LOG_TAG, "onSuccess $LOG_TAG")

            for (jo in productList) {

                val gson = GsonBuilder().setPrettyPrinting().create()
                val product: Product =
                    gson.fromJson(jo, Product::class.java)

                val linearLayoutManager = LinearLayoutManager(activity)
                recyclerView.layoutManager = linearLayoutManager

                adapter = GroupAdapter()
                section.add(
                    RecyclerProductItem(
                        activity as MainActivity,
                        Product(
                            product.id,
                            product.title,
                            product.description,
                            product.price
                        ), this
                    )
                ) 

                adapter.add(section)
                recyclerView.adapter = adapter
            }

        }

        override fun onFailure() {
            Log.e(LOG_TAG, "onFailure $LOG_TAG")
        }

        override fun onError() {
            Log.e(LOG_TAG, "onError $LOG_TAG")
        }

        override fun onClickItem(id: Int, position: Int) {
            section.remove(adapter.getItem(position))
        }

    })

}
Francislainy Campos
  • 3,462
  • 4
  • 33
  • 81
  • 2
    Ok I didn't realize groupie relies on position to remove. You probably don't need to pass the product id any more, just the position sorry. – Carson Holzheimer Jul 07 '20 at 06:24
  • 1
    No worries! Yes, I think I noticed it would probably still work without the id, but just didn't have time to remove it and double check it properly. Thanks again for your help. – Francislainy Campos Jul 07 '20 at 08:27