I'm trying to inject a dependency into a navigation graph scoped ViewModel
This is my app :
#HiltAndroidApp
class App : Application()
The activity hosting my fragment :
#AndroidEntryPoint
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findNavController(R.id.nav_host_fragment).navigate(R.id.main_graph)
}
}
My navigation graph :
<?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"
android:id="#+id/main_graph"
app:startDestination="#id/mainFragment">
<fragment
android:id="#+id/mainFragment"
android:name="com.example.hilt.MainFragment" />
</navigation>
The object I'm going to inject into my ViewModel :
class Repo #Inject constructor()
My ViewModel :
#HiltViewModel
class MainViewModel #Inject constructor(
val repo: Repo,
application: Application
) : AndroidViewModel(application) {
fun test(){}
}
The fragment into which I inject the view model :
#AndroidEntryPoint
class MainFragment : Fragment() {
val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.test()
}
}
Now at this point everything is working properly, but as soon as I try to scope the VM using this :
#AndroidEntryPoint
class MainFragment : Fragment() {
val viewModel: MainViewModel by navGraphViewModels(R.id.main_graph)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.test()
}
}
The app fails to create my ViewModel and crashes leaving this stacktrace :
Caused by: java.lang.RuntimeException: Cannot create an instance of class com.example.hilt.MainViewModel
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:269)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:112)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
at androidx.lifecycle.ViewModelLazy.getValue(ViewModelProvider.kt:54)
at androidx.lifecycle.ViewModelLazy.getValue(ViewModelProvider.kt:41)
at com.example.hilt.MainFragment.getViewModel(Unknown Source:2)
at com.example.hilt.MainFragment.onCreate(MainFragment.kt:17)
at androidx.fragment.app.Fragment.performCreate(Fragment.java:2936)
at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:472)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:278)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2177)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2094)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1990)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3122)
at androidx.fragment.app.FragmentManager.dispatchCreate(FragmentManager.java:3045)
at androidx.fragment.app.Fragment.onCreate(Fragment.java:1867)
at androidx.navigation.fragment.NavHostFragment.onCreate(NavHostFragment.java:264)
at androidx.fragment.app.Fragment.performCreate(Fragment.java:2936)
at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:472)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:278)
at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:141)
at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:135)
at androidx.fragment.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:313)
at androidx.fragment.app.FragmentActivity.onCreateView(FragmentActivity.java:292)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:780)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:863)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
at android.view.LayoutInflater.inflate(LayoutInflater.java:374)
at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:696)
at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:170)
Clearly the by navGraphViewModels shorthand is not using the right VM Factory and I'm a bit at a loss here.
Use hiltNavGraphViewModels from androidx.hilt:hilt-navigation-fragment instead of navGraphViewModels
https://developer.android.com/training/dependency-injection/hilt-jetpack#viewmodel-navigation
Related
I'm starting to learn dagger. I try to get a string from the ViewModel, an error occurs:
FATAL EXCEPTION: main
Process: com.sem.daggersimple, PID: 7882
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.sem.daggersimple/com.sem.daggersimple.presentation.MainActivity}: android.view.InflateException: Binary XML file line #26 in com.sem.daggersimple:layout/activity_main: Binary XML file line #26 in com.sem.daggersimple:layout/activity_main: Error inflating class fragment
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3450)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3602)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2067)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7697)
...
Caused by: android.view.InflateException: Binary XML file line #26 in com.sem.daggersimple:layout/activity_main: Binary XML file line #26 in com.sem.daggersimple:layout/activity_main: Error inflating class fragment
Caused by: android.view.InflateException: Binary XML file line #26 in com.sem.daggersimple:layout/activity_main: Error inflating class fragment
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property stringViewModel has not been initialized
at com.sem.daggersimple.presentation.StringFragment.getStringViewModel(StringFragment.kt:24)
at com.sem.daggersimple.presentation.StringFragment.onCreateView(StringFragment.kt:43)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2963)
...
at com.sem.daggersimple.presentation.MainActivity.onCreate(MainActivity.kt:20)
MainActivity:
class MainActivity : AppCompatActivity() {
#Inject
lateinit var stringViewModel: StringViewModel
private var binding: ActivityMainBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
appComponent.inject(this)
}
}
StringFragment:
class StringFragment : Fragment() {
private var binding : FragmentStringBinding? = null
#Inject
lateinit var stringViewModel: StringViewModel
override fun onAttach(context: Context) {
super.onAttach(context)
// Grabs the registrationComponent from the Activity and injects this Fragment
// (activity as MainActivity).
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_string, container, false)
// stringViewModel.getString.
Log.d("stringViewModel","stringViewModel = " + stringViewModel.getString.get(0))
binding?.textView?.text = stringViewModel.getString
return binding?.root
}
}
StringRepository:
class StringRepository #Inject constructor() {
override fun toString() : String = "какой-то текст"
}
StringUseCase:
class StringUseCase #Inject constructor(private val repository: StringRepository) {
fun getString() : String {
return repository.toString()
}
}
StringViewModel:
class StringViewModel #Inject constructor(private val stringUseCase: StringUseCase) : ViewModel() {
val getString = stringUseCase.getString()
}
App:
class App : Application() {
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent.create()
}
}
val Context.appComponent: AppComponent
get() = when(this) {
is App -> appComponent
else -> this.applicationContext.appComponent
}
AppComponent:
#Component(modules = [AppModule::class])
interface AppComponent {
fun inject(mainActivity: MainActivity)
fun inject(stringFragment: StringFragment)
}
AppModule:
#Module
object AppModule {
#Provides
fun provideStringViewModel(stringUseCase: StringUseCase) : StringViewModel {
return StringViewModel(stringUseCase)
}
#Provides
fun provideStringUseCase(repository: StringRepository) : StringUseCase {
return StringUseCase(repository)
}
#Provides
fun provideStringRepository() : StringRepository {
return StringRepository()
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout 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"
tools:context=".presentation.MainActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="#+id/framelayout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="#+id/rd_fragment"
android:name="com.sem.daggersimple.presentation.StringFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
fragment_string.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".presentation.StringFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:id="#+id/textView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="#string/hello_blank_fragment" />
</layout>
P.S. I don't use Hilt
You cannot use var for lazy initialization delegated properties. You should use val. You can use any of these to initialize view model.
val stringViewModel by viewModels<StringViewModel>()
val stringViewModel :StringViewModel by viewModels()
I tried to access NavController from the home fragment but had no success. Spent the whole day but still had no success at all. The app crashes in HomeFragment in this line of code:
navController = requireActivity().findNavController(R.id.nav_host_fragment)
Here is the code that I have implemented:
HomeFragmnet
class HomeFragment : Fragment() {
private lateinit var homeViewModel: HomeViewModel
private lateinit var recyclerView: RecyclerView
private lateinit var navController: NavController
private var itemList: MutableList<Home?> = ArrayList()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
navController = requireActivity().findNavController(R.id.nav_host_fragment)
homeViewModel = ViewModelProvider(this).get(HomeViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_home, container, false)
recyclerView = root.findViewById(R.id.homeRecyclerView)
return root
}
}
This is xml implementation of nav_bar.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/nav_graph"
app:startDestination="#id/nav_home">
<fragment
android:id="#+id/nav_home"
android:name="fi.gambitlabs.pworker.ui.home.HomeFragment"
android:label="#string/menu_home"
tools:layout="#layout/fragment_home" />
</navigation>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
layout="#layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.navigation.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:showIn="#layout/app_bar_main">
<fragment
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var navController: NavController
private lateinit var navView: NavigationView
override fun onCreate(savedInstanceState: Bundle?) {
// Disable dark mode
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
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)
navView = findViewById(R.id.nav_view)
navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(setOf(
R.id.nav_home), drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.main, menu)
return true
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
}
Error Logs
E/AndroidRuntime: FATAL EXCEPTION: main
Process: fi.gambitlabs.pworker, PID: 12300
java.lang.RuntimeException: Unable to start activity ComponentInfo{fi.gambitlabs.pworker/fi.gambitlabs.pworker.MainActivity}: android.view.InflateException: Binary XML file line #14 in fi.gambitlabs.pworker:layout/activity_main: Binary XML file line #19 in fi.gambitlabs.pworker:layout/content_main: Error inflating class fragment
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: android.view.InflateException: Binary XML file line #14 in fi.gambitlabs.pworker:layout/activity_main: Binary XML file line #19 in fi.gambitlabs.pworker:layout/content_main: Error inflating class fragment
Caused by: android.view.InflateException: Binary XML file line #19 in fi.gambitlabs.pworker:layout/content_main: Error inflating class fragment
Caused by: java.lang.IllegalArgumentException: ID does not reference a View inside this Activity
at android.app.Activity.requireViewById(Activity.java:3375)
at androidx.core.app.ActivityCompat.requireViewById(ActivityCompat.java:366)
at androidx.navigation.Navigation.findNavController(Navigation.java:58)
at *emphasized text*androidx.navigation.ActivityKt.findNavController(Activity.kt:30)
at fi.gambitlabs.pworker.ui.home.HomeFragment.onCreateView(HomeFragment.kt:30)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2963)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:518)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:112)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1647)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3128)
at androidx.fragment.app.FragmentManager.dispatchViewCreated(FragmentManager.java:3065)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:2988)
at androidx.fragment.app.FragmentStateManager.ensureInflatedView(FragmentStateManager.java:392)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:281)
at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:140)
at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:135)
at androidx.fragment.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:319)
at androidx.fragment.app.FragmentActivity.onCreateView(FragmentActivity.java:298)
at android.view.LayoutInflater.tryCreateView(LayoutInflater.java:1067)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:995)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:959)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1121)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
at android.view.LayoutInflater.parseInclude(LayoutInflater.java:1261)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1117)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
E/AndroidRuntime: at android.view.LayoutInflater.parseInclude(LayoutInflater.java:1261)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1117)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
at android.view.LayoutInflater.inflate(LayoutInflater.java:680)
at android.view.LayoutInflater.inflate(LayoutInflater.java:532)
at android.view.LayoutInflater.inflate(LayoutInflater.java:479)
at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:699)
at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:195)
at fi.gambitlabs.Pworker.MainActivity.onCreate(MainActivity.kt:28)
at android.app.Activity.performCreate(Activity.java:8000)
at android.app.Activity.performCreate(Activity.java:7984)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
app_bar_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/Theme.PWorker.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/Theme.PWorker.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="#layout/content_main" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Dependencies
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.navigation:navigation-fragment:2.3.5'
implementation 'androidx.navigation:navigation-ui:2.3.5'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
//Firebase
implementation platform('com.google.firebase:firebase-bom:28.2.0')
implementation 'com.google.firebase:firebase-analytics-ktx'
implementation 'com.google.firebase:firebase-crashlytics-ktx'
}
Have spent lots of time trying to find a solution on google but nothing works and now I am stuck. It crashes when trying to access requireActivity().findNavController(R.id.nav_host_fragment) in HomeFragment but no crashes with the same code in MainActivity as seen on code snippet.
Any help is highly appreciable.
I tried to access NavController from the home fragment but had no success.
You can't access the NavController because you try to access it before the setContentView() of the activity get finished; i.e you are trying to access a resource R.id.nav_host_fragment that doesn't exist yet.
And the reason because the HomeFragment is initially hosted by the activity, and it's a part of the activity layout; and in order to draw the fragment on the activity, it needs to take a chance first to get the fragment layout before returning the entire layout with setContentView()
To verify that add the below log messages in the activity (MainActivity) & HomeFragment:
Activity:
class MainActivity : AppCompatActivity() {
// .... Code omitted
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "MainActivity onCreate: BEFORE setContentView()")
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
Log.d(TAG, "MainActivity onCreate: AFTER setContentView()")
// .... Code omitted
Log.d(TAG, "MainActivity onCreate: END")
}
Fragment:
class HomeFragment : Fragment() {
// .... Code omitted
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
Log.d(TAG, "HomeFragment onCreateView: START")
homeViewModel =
ViewModelProvider(this).get(HomeViewModel::class.java)
// .... Code omitted
Log.d(TAG, "HomeFragment onCreateView: END")
return root
}
The outcome:
D/LOG_TAG: MainActivity onCreate: BEFORE setContentView()
D/LOG_TAG: HomeFragment onCreateView: START
D/LOG_TAG: HomeFragment onCreateView: END
D/LOG_TAG: MainActivity onCreate: AFTER setContentView()
D/LOG_TAG: MainActivity onCreate: END
How to solve this:
You can set the navController in lifecycle methods beyond onCreateView() & onViewCreated() of the HomeFragment like in onStart(), or onResume()
class HomeFragment : Fragment() {
// .... Code omitted
override fun onStart() {
super.onStart()
val navController = requireActivity().findNavController(R.id.nav_host_fragment)
}
Seems like you haven't included content_main.xml in your activity_main.xml
I initialized viewModel with this line viewModel = (activity as NewsActivity).viewModel in BreakingNewsFragment but, recived below error.
How i can fix this problem?! thanks for your help
BreakingNewsFragment
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.View
import com.example.simplenewsapp.R
import com.example.simplenewsapp.main.MainViewModel
import com.example.simplenewsapp.NewsActivity
import com.example.simplenewsapp.adapter.NewsRecyclerViewAdapter
import kotlinx.android.synthetic.main.fragment_breaking_news.*
class BreakingNewsFragment : Fragment(R.layout.fragment_breaking_news) {
lateinit var viewModel: MainViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = (activity as NewsActivity).viewModel
....
NewsActivity
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import com.example.simplenewsapp.data.local.ArticleDatabase
import com.example.simplenewsapp.main.MainRepository
import com.example.simplenewsapp.main.MainViewModel
import com.example.simplenewsapp.main.MainViewModelProviderFactory
import kotlinx.android.synthetic.main.activity_news.*
class NewsActivity : AppCompatActivity() {
lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_news)
val newsRepository = MainRepository(ArticleDatabase(this))
val mainViewModelProviderFactory = MainViewModelProviderFactory(newsRepository)
viewModel =
ViewModelProvider(this, mainViewModelProviderFactory).get(MainViewModel::class.java)
bottomNavigationView.setupWithNavController(newsNavHostFragment.findNavController())
}
}
MainViewModelProviderFactory
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
class MainViewModelProviderFactory(val mainRepository:MainRepository): ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return MainViewModel(mainRepository) as T
}
}
MainViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.simplenewsapp.data.local.models.NewsResponse
import com.example.simplenewsapp.util.Resource
import kotlinx.coroutines.launch
import retrofit2.Response
class MainViewModel(
val newsRepository: MainRepository
) : ViewModel() {
val breakingNews: MutableLiveData<Resource<NewsResponse>> = MutableLiveData()
var breakingNewsPage = 1
init {
getBreakingNews("us")
}
fun getBreakingNews(countyCode: String) = viewModelScope.launch {
breakingNews.postValue(Resource.Loading())
val response = newsRepository.getBreakingNews(countyCode, breakingNewsPage)
breakingNews.postValue(handleBreakingNewsResponse(response))
}
private fun handleBreakingNewsResponse(response: Response<NewsResponse>): Resource<NewsResponse> {
if (response.isSuccessful) {
response.body()?.let { resultResponse ->
return Resource.Success(resultResponse)
}
}
return Resource.Error(response.message())
}
}
Error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.simplenewsapp, PID: 4455
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.simplenewsapp/com.example.simplenewsapp.NewsActivity}: android.view.InflateException: Binary XML file line #17: Binary XML file line #17: Error inflating class fragment
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: android.view.InflateException: Binary XML file line #17: Binary XML file line #17: Error inflating class fragment
Caused by: android.view.InflateException: Binary XML file line #17: Error inflating class fragment
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property viewModel has not been initialized
at com.example.simplenewsapp.NewsActivity.getViewModel(NewsActivity.kt:16)
at com.example.simplenewsapp.ui.fragments.BreakingNewsFragment.onViewCreated(BreakingNewsFragment.kt:29)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:2974)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:543)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:112)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1636)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3112)
at androidx.fragment.app.FragmentManager.dispatchViewCreated(FragmentManager.java:3049)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:2975)
at androidx.fragment.app.FragmentStateManager.ensureInflatedView(FragmentStateManager.java:389)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:281)
at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:141)
at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:135)
at androidx.fragment.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:313)
at androidx.fragment.app.FragmentActivity.onCreateView(FragmentActivity.java:292)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:780)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:863)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:866)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
at android.view.LayoutInflater.inflate(LayoutInflater.java:374)
at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:696)
at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:170)
at com.example.simplenewsapp.NewsActivity.onCreate(NewsActivity.kt:20)
at android.app.Activity.performCreate(Activity.java:7136)
at android.app.Activity.performCreate(Activity.java:7127)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
I/Process: Sending signal. PID: 4455 SIG: 9
Firstly why you are using same View Model for fragment and activity ?
This approach is incorrect and may lead to inconsistent data and memory leaks.
Ideally fragment and activity should have their own independent View Model.
For example:
a) NewsActivity and NewsViewModel
b) BreakingNewsFragment and BreakingNewsViewModel
This separates the code by following the Separation of concerns principle and you will get benefit from this as your application logic grows.
And If you are looking for shared behavior/data, please used Shared View Model.
Secondly, instead of using the View Model from NewsActivity
you should initialize it completely in BreakingNewsFragment, the way you did in NewsActivity.
// Add below code either in your fragment or activity.
val newsRepository = MainRepository(ArticleDatabase(this))
val mainViewModelProviderFactory = MainViewModelProviderFactory(newsRepository)
viewModel = ViewModelProvider(this, mainViewModelProviderFactory).get(MainViewModel::class.java)
I know it's late but I'm happy to find the solution:
The problem was fragment tag of xml.
I changed the fragment tag to androidx.fragment.app.FragmentContainerView.
and initializing the fragmentcontentview and setting up nav controller:
val navHostFragment= supportFragmentManager.findFragmentById(R.id.newsNavHostFrag) as NavHostFragment
val navController= navHostFragment.navController
bottomNavigationView.setupWithNavController(navController)
Use this dependency instead:
implementation 'androidx.appcompat:appcompat:1.2.0'
and use this version only.
I used to use implementation 'androidx.appcompat:appcompat:1.3.1', and that creates the same error as you are facing.
Try once and use implementation 'androidx.appcompat:appcompat:1.2.0' dependency and exact the same version - that's what worked for me.
If you are using fragment like this:-
<FrameLayout
android:id="#+id/flFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/bottomNavigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<fragment
android:id="#+id/newsNavHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/news_nav_graph" />
</FrameLayout>
then you have to change your appcompat dependency version 1.3.1 to 1.2.0 like this
implementation 'androidx.appcompat:appcompat:1.2.0'
but if you are using latest dependency version i.e 1.3.1 then you have to use
<androidx.fragment.app.FragmentContainerView
android:id="#+id/newsNavHostFrag"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/news_nav_graph"/>
and in MainActivity.kt
change this :-
bottomNavigationView.setupWithNavController(newsNavHostFragment.findNavController())
to this:-
val navHostFragment= supportFragmentManager.findFragmentById(R.id.newsNavHostFrag) as NavHostFragment
val navController= navHostFragment.navController
bottomNavigationView.setupWithNavController(navController)
Use below line instead of lateinit var viewModel: MainViewModel
private val viewModel:MainViewModel by activityViewModels()
Also , if you can't import it check for this dependency in app level build.gradle
implementation 'androidx.fragment:fragment-ktx:1.1.0'
and remove this line : viewModel = (activity as NewsActivity).viewModel
For solving this error, you have to put this code in your activity as well as in your fragment both
in Newsactivity:-
val newsResporiaty = NewsResporiaty(ArticleDatabase(requireContext()))
val viewModelFactory = NewsViewModelProviderFactory(newsResporiaty)
viewModel = ViewModelProvider(this,viewModelFactory).get(NewsViewModel::class.java)
And also your in fragmets:-
val newsResporiaty = NewsResporiaty(ArticleDatabase(requireContext()))
val viewModelFactory = NewsViewModelProviderFactory(newsResporiaty)
viewModel = ViewModelProvider(this,viewModelFactory).get(NewsViewModel::class.java)
This worked for me.
What I would like to do
I would like to call Activity from Fragment to realize both transitions among fragments and activities simultaneously in an Android app.
I have succeeded to call one specific method on activity file from fragment file, but I have trouble with calling an entire activity from a fragment.
Basic Functions on each page
SampleFragment - MainActivity's fragmentMethod() is called once the button is clicked on the page
Sample1Fragment - FormActivity is called once the button is clicked on the page
FormActivity - FormActivity2 is called once the button is clicked on the page
Errors and Problems
On Sample1Fragment.kt, I tried to call Entire FormActivity on the below part.
However the error message is shown and I have no idea to fix it to call the entire activity rather than a method on an activity.
Unresolved reference: AppCompatActivity
The part where I tried to call FromActivity from Sample1Fragment
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
btnClick2.setOnClickListener(object:View.OnClickListener{
// here I would like to move to FormActivity
override fun onClick(v: View?) {
(activity as FormActivity).AppCompatActivity()
}
})
}
Current Code
MainActivity.kt
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// make the list of fragment
val fragmentList = arrayListOf<Fragment>(
SampleFragment(),
Sample1Fragment()
)
// create instance of adapter
val adapter = SamplePagerAdapter(supportFragmentManager, fragmentList)
/// set adapter
viewPager.adapter = adapter
}
public fun fragmentMethod() {
Toast.makeText(this#MainActivity, "Method called From Fragment", Toast.LENGTH_LONG).show();
}
}
SamileFragment.kt
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.fragment_sample.*
class SampleFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_sample, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
btnClick.setOnClickListener(object:View.OnClickListener{
override fun onClick(v: View?) {
(activity as MainActivity).fragmentMethod()
}
})
}
}
Sample1Fragment.kt
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.fragment_sample1.*
class Sample1Fragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_sample1, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
btnClick2.setOnClickListener(object:View.OnClickListener{
// here I would like to move to FormActivity
override fun onClick(v: View?) {
(activity as FormActivity).AppCompatActivity()
}
})
}
}
SamplePagerAdapter.kt
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
class SamplePagerAdapter(fm: FragmentManager, private val fragmentList: List<Fragment>) :
FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
// control fragment to show
override fun getItem(position: Int): Fragment {
return fragmentList[position]
}
// the size of contents(fragment list) to set viewPager
override fun getCount(): Int {
return fragmentList.size
}
}
FormActivity.kt
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.app.Activity
import android.content.Intent
import android.util.Log
import kotlinx.android.synthetic.main.activity_form.*
class FormActivity : AppCompatActivity() {
companion object {
const val EXTRA_MESSAGE = "com.example.kotlinactivitydatatrans.MESSAGE"
}
private val RESULT_SUBACTIVITY = 1000
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
if (editText.text != null) {
val intent = Intent(applicationContext, FormActivity2::class.java)
val str = editText.text.toString()
Log.d("debug",str)
intent.putExtra(EXTRA_MESSAGE, str)
startActivityForResult(intent, RESULT_SUBACTIVITY)
editText.setText("")
}
}
}
// to get a result form SubActivity
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
super.onActivityResult(requestCode, resultCode, intent)
if (resultCode == Activity.RESULT_OK &&
requestCode == RESULT_SUBACTIVITY && intent != null) {
val res = intent.extras?.getString(EXTRA_MESSAGE)?: ""
textView.text = res
}
}
}
What I can do now
I can call MainActivity.fragmentMethod() from SampleFragment and transition between SampleFragment and Sample1Fragment with the following reference.
Reference: Kotlin-How to call an activity method from fragment in Android App?
Developping Environment
Android Studio 3.6.1
What I tried to do after reading comments
New Trouble
It was succeeded to build a project and run the app on the emulator.
However, the screen was suddenly shut down when I click the button to call FormActivity on Sample1Fragment.
There is no error on codes so I cannot find any solutions.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.fragact">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".FormActivity"
android:label="#string/app_name"
tools:ignore="WrongManifestParent">
</activity>
<activity android:name=".FormActivity2"
android:label="#string/app_name"
tools:ignore="WrongManifestParent">
</activity>
</application>
</manifest>
fragment_sample1.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Sample1Fragment">
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:text="Sample1Fragment"
android:textSize="36sp" />
<Button
android:id="#+id/btnClick2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_gravity="center"
android:background="#color/colorPrimary"
android:textColor="#FFF"
android:textAllCaps="false"
android:minEms="8"
android:text="Call Activity"/>
</FrameLayout>
Logcat
2020-03-10 18:50:48.622 9917-9917/com.example.fragact W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy#f79aa98
2020-03-10 20:50:48.659 9917-9917/com.example.fragact D/AndroidRuntime: Shutting down VM
2020-03-10 20:50:48.661 9917-9917/com.example.fragact E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.fragact, PID: 9917
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.fragact/com.example.fragact.FormActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at com.example.fragact.FormActivity.onCreate(FormActivity.kt:22)
at android.app.Activity.performCreate(Activity.java:7136)
at android.app.Activity.performCreate(Activity.java:7127)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
2020-03-10 20:50:48.679 9917-9917/com.example.fragact I/Process: Sending signal. PID: 9917 SIG: 9
I think the problem here is that you're trying to set onClick listener too early.
Fragments, unlike Activities, has different lifecycle and situations when their View isn't created yet (or already destroyed) is much more easy to get.
Check out this documentation for more info.
In general - best way to start interacting with views is the onViewCreated lifecycle callback. Here, and afterward, you can be sure that the view hierarchy is ready.
In your example, you've used onActivityCreated callback to start interacting with views. In particular, setting onClick listener - btnClick.setOnClickListener(...) in the SampleFragment. And in the first fragment, I assume, it worked well because its creation was "aligned" with activity creation.
But in the case of the second fragment - Sample1Fragment - activity was already created, which probably resulted in wrong method ordering. I assume, onActivityCreated was called earlier that onViewCreated (on onCreateView). And calling btnClick2.setOnClickListener(...) resulted in crash, cause btnClick2 isn't initialized yet.
My suggestion is to move view related code of your Fragments into onViewCreated method(s). Having something like this:
override fun onViewCreated(view: View, savedInstanceState: Bundle) {
btnClick2.setOnClickListener(...)
}
I want to inject data binding to activity using Dagger 2. I have a problem with its. I don't understand what is wrong.
ERROR
cannot access DataBindingComponent class file for androidx.databinding.DataBindingComponent not found
And my code:
MainActivity.kt
class MainActivity : AppCompatActivity() {
#Inject lateinit var bindingUtil: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
val stepperContainer = (applicationContext as StepperApplication).stepperContainer
stepperContainer.inject(this)
super.onCreate(savedInstanceState)
}
}
StepperApplication.kt
class StepperApplication : Application() {
val stepperContainer by lazy {
DaggerStepperComponent.builder().context(ContextModule(applicationContext)).build()
}
override fun onCreate() {
super.onCreate()
}
}
StepperComponent.kt
#Singleton
#Component (modules = [ContextModule::class])
interface StepperComponent {
fun inject(activity: MainActivity)
}
ContextModule.kt
#Module
class ContextModule (var context: Context) {
#Singleton
#Provides
fun context() : Context {
return context
}
}
ActivityMainModule.kt
#Module (subcomponents = [ActivityMainComponent::class])
class ActivityMainModule (var activity: MainActivity) {
#Provides
fun activityMainBinding(activity: MainActivity) : ActivityMainBinding {
return DataBindingUtil.setContentView(activity, R.layout.activity_main)
}
}
ActivityMainComponent.kt
#Subcomponent
interface ActivityMainComponent {
#Subcomponent.Builder
interface Builder {
fun activityMain(activity: MainActivity) : Builder
fun build() : ActivityMainComponent
}
}
Gradle
implementation 'com.google.dagger:dagger:2.25.2'
kapt 'com.google.dagger:dagger-compiler:2.25.2'
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
What is the problem and how to solve it?