1

I have three fragments and all of them have two functions which are EXACTLY THE SAME. My problem is that I (obviously) don't want to copy the same two functions over and over again and put it in the fragments.

Ain't there a way to put these functions in one place and call them from my fragments? These functions have to do with navController and Toolbar Navigation so I cant put it in my viewmodel. Another solution could be to create a Baseclass fragment, put these functions in there and inherit from it?

Functions

private fun initProgressbar(currentStateNumber: StateProgressBar.StateNumber, progressBarDescription: ArrayList<String>) =
    state_progress_bar.apply {
        setStateDescriptionData(progressBarDescription)
        setCurrentStateNumber(currentStateNumber)
    }

private fun initToolbar(navController: NavController, appBarConf: AppBarConfiguration, textToolbar: String?) =
    toolbar.apply {
        setupWithNavController(navController, appBarConf)
        toolbar_title.text = textToolbar
    }

I didn't found any solution without violating the mvvm architecture or the lifecyle of the fragments. I am using jetpack navigation, mvvm and dagger hilt.

I appreciate every help.

EDIT: Possible Solution

abstract class BaseFragment(
    layout: Int,
    private val progressBarDescription: ArrayList<String>,
    private val stateNumber: StateProgressBar.StateNumber
) : Fragment(layout) {

    private val _navController: NavController by lazy { findNavController() }
    private val appBarConf by lazy { AppBarConfiguration(_navController.graph) }
    private val calibrateRepairToolbarText by lazy { arguments?.getString("calibrate_repair_toolbar_text") }

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

    val navController: NavController
        get() = _navController

    fun initProgressbar(): StateProgressBar = state_progress_bar.apply {
            setStateDescriptionData(progressBarDescription)
            setCurrentStateNumber(stateNumber)
        }

    fun initToolbar(): MaterialToolbar = toolbar.apply {
            setupWithNavController(_navController, appBarConf)
            toolbar_title.text = calibrateRepairToolbarText
        }
}

And then in the other Fragment:

class Fragment(
    private val progressBarDescription: ArrayList<String>,
    @StateNumberOne private val stateNumber: StateProgressBar.StateNumber
) : BaseFragment(
    R.layout.fragment_calibrate_repair_message,
    progressBarDescription,
    stateNumber
) {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
    }

}
Andrew
  • 4,264
  • 1
  • 21
  • 65

2 Answers2

1

You can create a baseFragment, with these methods, and then have your three Fragments extend baseFragment

Tal Mantelmakher
  • 994
  • 1
  • 7
  • 23
  • But is that the best option? Will it violate the lifecycle when I access the view from this baseFragment? I actually created an abstract baseFragment but I don't know about the lifecycle thing. – Andrew Aug 16 '20 at 14:11
  • 1
    I'd advise not making such assumptions with Fragments. – EpicPandaForce Aug 16 '20 at 14:13
  • 1
    It doesnt affect lifecycle at all. You will nevr access view *from* the baseFragment, your child fragments will simply inherit these methods, and you'll be able to use these methods in your child fragments, as if they are there – Tal Mantelmakher Aug 16 '20 at 14:13
  • @Tal Mantelmakher I've edited my post. Could you tell me if it is okay to call onViewCreated() in my BaseFragment and call my initProgessBar() and initToolbar() methods in there? Will that break anything? – Andrew Aug 16 '20 at 15:10
  • 1
    Looks great. I don't fully understand all the methods and variables you used there, where they are defined and what they do, but as long as they didn't break anything themselves until now, they shouldn't break anything now. – Tal Mantelmakher Aug 16 '20 at 19:07
0

The solution is that these initToolbar methods belong not to the Fragment, but to the Toolbar. Hence why it has initToolbar in it.

So if you can guarantee that whenever this method is used on any callsite, then it makes sense, then you can create a top-level extension function for the Toolbar.

Otherwise, you could extend Toolbar and use a "compound ViewGroup" to contain the shared view logic.

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • 1
    I have created these initToolbar methods, don't get distracted by their names. Is creating top level extensions functions better than creating a baseFragment and putting these methods in there? I can make sure and guarantee that whenever these methods are called, that it makes sense in both cases – Andrew Aug 16 '20 at 14:18
  • I'd primarily recommend to refer to https://github.com/frogermcs/InstaMaterial/blob/Post-8/app/src/main/java/io/github/froger/instamaterial/ui/view/FeedContextMenu.java#L16 for inspiration on how to create your own "custom component", a toolbar that knows more – EpicPandaForce Aug 16 '20 at 15:06