Search code examples
androidandroid-fragmentsandroid-jetpackandroid-jetpack-navigation

Is it right to put replaceFragment inside onBindViewHolder?


I'm learning Jetpack by changing the Demo from codelabs.

What I changed is move the code of the MainActivity.kt into a fragment and jump between fragments.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        replaceFragment(WordListFragment())
    }
}

// Extension function to replace fragment
fun AppCompatActivity.replaceFragment(fragment: Fragment){
    val fragmentManager = supportFragmentManager
    val transaction = fragmentManager.beginTransaction()
    transaction.replace(R.id.host,fragment)
    transaction.addToBackStack(null)
    transaction.commit()
}

When we click the items, we will call replaceFragment inside WordListAdapter and go to another fragment as follows:

enter image description here

class WordListAdapter : RecyclerView.Adapter<WordListAdapter.WordViewHolder>() {
    ...
    override fun onBindViewHolder(holder: WordViewHolder, position: Int) {

        val current = words[position]
        holder.wordItemView.text = current.word

        holder.itemView.setOnClickListener {
            // fire recyclerView click event
            val activity = it.context as AppCompatActivity
            val args = Bundle()
            // Send string data as key value format
            args.putString("word", current.word)
            val fragment = WordDefinitionFragment()
            fragment.arguments = args
            activity.replaceFragment(fragment)

        }
    }

I just wondering if it's the right way to put replaceFragment inside the onBindViewHolder ?


Solution

  • In my opinion RecyclerView.Adapter should only bind immutable data and pass clicks via callbacks, and the Activity should be the one to change fragment. In my opinion you should do something like this:

    class WordListAdapter(private val onViewHolderClicked: (String) -> Unit) : RecyclerView.Adapter<WordListAdapter.WordViewHolder>() {
        ...
        override fun onBindViewHolder(holder: WordViewHolder, position: Int) {
    
            val current = words[position]
            holder.wordItemView.text = current.word
    
            holder.itemView.setOnClickListener {
                onViewHolderClicked(current.word)
            }
        }
    

    and in Activity:

    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            replaceFragment(WordListFragment())
        }
    ...
        fun setupRecyclerView() {
            ...
            val adapter = WordListAdapter() { word ->
                val args = Bundle()
                // Send string data as key value format
                args.putString("word", word)
                val fragment = WordDefinitionFragment()
                fragment.arguments = args
                replaceFragment(fragment)
            }
        }
    }