Reputation: 1078
I have created Navigation Drawer Activity:
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
}
And I have mobile_navigation.xml
:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mobile_navigation"
app:startDestination="@id/databaseFragment">
<fragment
android:id="@+id/databaseFragment"
android:name="com.acmpo6ou.myaccounts.ui.DatabaseFragment"
android:label="fragment_database_list"
tools:layout="@layout/fragment_database_list" >
<action
android:id="@+id/actionCreateDatabase"
app:destination="@id/createDatabaseFragment" />
</fragment>
<fragment
android:id="@+id/createDatabaseFragment"
android:name="com.acmpo6ou.myaccounts.ui.CreateDatabaseFragment"
android:label="create_edit_database_fragment"
tools:layout="@layout/create_edit_database_fragment" />
</navigation>
The start destination is DatabaseFragment
. However there is a problem, here is my DatabaseFragment
:
class DatabaseFragment(
override val adapter: DatabasesAdapterInter,
val presenter: DatabasesPresenterInter
) : Fragment(), DatabaseFragmentInter {
...
companion object {
@JvmStatic
fun newInstance(
adapter: DatabasesAdapterInter,
presenter: DatabasesPresenterInter
) = DatabaseFragment(adapter, presenter)
}
}
As you can see my DatabaseFragment
should receive two arguments to its constructor: adapter
and presenter
. This is because of dependency injection, in my tests I can instantiate DatabaseFragment
passing through mocked adapter
and presenter
. Like this:
...
val adapter = mock<DatabasesAdapterInter>()
val presenter = mock<DatabasesPresenterInter>()
val fragment = DatabaseFragment(adapter, presenter)
...
It works with tests, but it doesn't work with android navigation. It seems that Android Navigation Components create DatabaseFragment
instead of me, but they don't pass any arguments to fragment's constructor and it fails with error that is too long to post it here.
Is there a way to tell Navigation Components so that they pass appropriate arguments to my fragments when instantiating them?
Thanks!
Upvotes: 1
Views: 4199
Reputation: 1078
I fixed everything pretty easily using default arguments, like this:
class DatabaseFragment(
override val adapter: DatabasesAdapterInter = DatabasesAdapter(),
val presenter: DatabasesPresenterInter = DatabasesPresenter()
) : Fragment(), DatabaseFragmentInter {
...
companion object {
@JvmStatic
fun newInstance() = DatabaseFragment()
}
}
Upvotes: 0
Reputation: 498
I just want to add to i30mb1 answer:
Is there really a necessity for you to pass those two arguments in the constructor?
As far as I know and as far as I have experimented with MVP, each view should have a presenter. So for example when I create a new fragment, I create a new presenter for it. Then the parent activity should have another presenter. If you need that presenter so that the fragment can make changes in the Activities view, you could implement interfaces, but that's another topic.
If you ever need to pass simple arguments using navigation like POJOS or even simplier objects like Strings etc.. you can use SafeArgs https://developer.android.com/guide/navigation/navigation-pass-data
Upvotes: 1
Reputation: 4776
Short answer is no, you can not pass arguments to Fragment
.
All subclasses of Fragment
must include a public no-argument constructor. The framework will often re-instantiate a fragment class when needed, in particular during state restore, and needs to be able to find this constructor to instantiate it. If the no-argument constructor is not available, a runtime exception will occur in some cases during state restore.
Upvotes: 2