Lucas
Lucas

Reputation: 643

How to dynamically update RecyclerView within AndroidView in Android Compose

I want the RecyclerView to dynamically change when the list is altered.

The view is recomposed based on the AndroidView.update expression. However nothing I have tried works. I am not familiar with RecyclerViews and I think that is the issue.

Any ideas?

 AndroidView(
        factory = {

            // Logic for inflating a RecyclerView in a normal Android View System
            val view = LayoutInflater.from(it)
                .inflate(R.layout.edit_list_xml_screen_layout, null, false)

            val recyclerView = view.findViewById<RecyclerView>(R.id.taskListRV)
            recyclerView.layoutManager = LinearLayoutManager(it)

            val adapter = TaskRecyclerViewAdaptor(it, taskList)
            // adapter.setClickListener()
            recyclerView.adapter = adapter
            recyclerView.addItemDecoration(
                DividerItemDecoration(
                    it,
                    DividerItemDecoration.VERTICAL
                )
            )
            
            recyclerView                    // Return recyclerView
        },
        update = {
            /* TODO dynamically update recycler view */
        }

Bonus points if someone can tell me what argument to add to adapter.setClickListener()

Upvotes: 2

Views: 2821

Answers (2)

mochadwi
mochadwi

Reputation: 1268

In our case:

  1. We use ViewBinding in our Fragment, therefore we need to remove the parent group first (cmiiw in this part), we use this ViewBindingDelegate here
  2. The click listener also passed as argument, see onItemClick
  3. In our case, the recyclerview setup was done in OurFragment instead of inside Compose screen, so the compose screen only receive the recyclerView: (Context) -> View lambda to inflate the view inside Compose screen OurComposeScreen.kt

Our Fragment

// OurFragment.kt

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

    private fun initBanner() {
        binding.banner.setContent {
            BannerListScreen(
                recyclerView = { context ->
                    recyclerViewSetup(
                        context = context,
                        onItemClick = ::recyclerViewClick,
                    )
                },
            )
        }
    }

// ...

    private fun recyclerViewSetup(
        context: Context,
        // modify this to your usecase, as we use RecyclerView.OnItemTouchListener, but this is also possible to use a simple lambda () -> Unit
        onItemClick: (Context, MyAdapter) -> RecyclerView.OnItemTouchListener,
    ): RecyclerView = with(LayoutInflater.from(context).inflate(R.layout.your_custom_xml, null, false)) {
        val recyclerView = RecyclerView(context).apply {
            if (parent != null) (parent as ViewGroup).removeView(this) // we need to remove the parent group as it'll inflate the view twice if added in compose
            layoutManager = LinearLayoutManager(context).apply {
                orientation = LinearLayoutManager.HORIZONTAL
            }

            adapter = MyAdapter(...)
            adapter.notifyDataSetChanged()
            addOnItemTouchListener(onItemClick(context, adapter))
        }

        recyclerView
    }


    private fun recyclerViewClick(
        context: Context,
        adapter: MyAdapter,
    ) = RecyclerItemClickListener(
        context,
        RecyclerItemClickListener.OnItemClickListener { view, position ->
            val b = adapter.getItem(position) // todo something with the click listener
        }
    )

Our Compose screen:

// OurComposeScreen.kt
@Composable
internal fun BannerReminder(
    // .....
    recyclerView: (Context) -> View,
) {
    Column(
        // omitted....
    ) {
        AndroidView(
            modifier = /* ... */,
            factory = { context -> recyclerView(context) }
        )
    }
}

Upvotes: 3

LaC
LaC

Reputation: 61

Here, the update block gets called whenever there is change in list "size". Like an item add, remove or clearing list. Considering "taskList" - initially the recycler view is populated on given list and both factory and update block are called for first time.

First update list:

taskList.addAll(itemsToBeAdded)

Notify adapter:

update = {
     (it.adapter as TaskRecyclerViewAdaptor).notifyDataSetChanged()
    }

To be specific on add or remove item, you can notify it like

UpdateList as:

taskList.add(item)
itemposition = position

or

taskList.remove(item)
itemposition = position

Then in update:

 update = {
     (it.adapter as TaskRecyclerViewAdaptor).notifyItemRemoved(itemposition)
    }

Every time on change in list size, the update method gets called.

Upvotes: 2

Related Questions