I am receiving notification from firebase, it navigates correctly if receive it on foreground or background. But if I tap on notification from blocking screen it does not work. I think the problem is from not finding navController. But don't know what to do
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.MainTheme)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
mainviewModel.navigateToMainFragmentEvent.observe(this, EventObserver {
Log.d("A","notification")`
Navigation.findNavController(this, R.id.nav_host_fragment)
.navigate(R.id.action_global_mainFragment)
})
}
}
SplashFragment is a homeFragment
class SplashFragment : Fragment(R.layout.activity_splash) {
val splashViewModel: SplashViewModel by sharedViewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
getStringExtra(NOTIFICATION_TYPE)?.let {
splashViewModel.notificationRecived(it)
}
}
}
when I debug this code:
Navigation.findNavController(this, R.id.nav_host_fragment)
IllegalStateException: ID does not reference a View inside this Activity
Related
I have 2 fragments that i navigate throught navigation component from Jetpack. When i go back from fragment 2 to fragment 1, i fire an event throught livedata from fragment 2 and fragment1 should be observing. Althought, fragment 1 didnt observed and when i sste a breakpoint to check how many observers liveData has, it say 0.
Code below
class Fragment1 :
Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
myViewModel.selectedItem.observe(viewLifecycleOwner, {
println("Observed")
})
findNavController().navigate(
Fragment1Directions.actionToFragment2()
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
myViewModel = ViewModelProvider(requireActivity()).get(MyViewModel::class.java)
}
private lateinit var myViewModel: MyViewModel
}
class Fragment2 :
Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
myButton.setOnClickListener{
MyViewModel().selectItem(true)
findNavController().navigateUp()
}
}
}
class MyViewModel : ViewModel() {
private val mutableSelectedItem = MutableLiveData<Boolean>()
val selectedItem: LiveData<Boolean> get() = mutableSelectedItem
fun selectItem(value: Boolean) {
mutableSelectedItem.value = value
}
}
It's strange to not have observer for
myViewModel.selectedItem.observe(viewLifecycleOwner, {
println("Observed")
})
when it's called onViewCreated and with viewLifecycleOwner, but it looks like you have a mistake for creating common ViewModel for Fragment1 and Fragment2
Both should use
myViewModel = ViewModelProvider(requireActivity()).get(MyViewModel::class.java) to get same ViewModel for both fragments
I have an app with 3 main destinations which can be accessed by a bottom nav view. Each destination has its own navigation graph.
The problem is that when I minimize and reopen my app, the navigation components reset to the default destination. Why does this happen?
My main activity: (Irrelevant code omitted)
class MainActivity : AppCompatActivity() {
// List of base host containers
val fragments = listOf(
HomeHostFragment(),
CoursesHostFragment(),
SearchHostFragment()
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val viewPager: ViewPager = findViewById(R.id.main_activity_pager)
viewPager.adapter = ViewPagerAdapter()
}
inner class ViewPagerAdapter : FragmentPagerAdapter(supportFragmentManager) {
override fun getItem(position: Int): Fragment =
fragments[position]
override fun getCount(): Int =
fragments.size
}
}
HomeHostFragment.kt:
class HomeHostFragment : Fragment() {
private lateinit var navController: NavController
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
inflater.inflate(R.layout.fragment_home_host, container, false)
override fun onStart() {
super.onStart()
navController = requireActivity().findNavController(R.id.nav_host_home)
navController.setGraph(R.navigation.nav_graph_home)
NavigationUI.setupWithNavController(toolbar_home, navController)
}
fun onBackPressed(): Boolean {
return navController.navigateUp()
}
}
Whenever onStart() is called, NavigationUI.setupWithNavController() is called again which resets the navigation. Move this call to onViewCreated() so that the navigation setup is not done every time the fragment pauses and restarts.
I want to add my custom layout to the side of the existing Toolbar, keeping all features with NavigationUI (Up button, animations, placing up buttons and drawers, etc)
How can i achieve this?
After some research i`ve found out the solution
MainActivity.kt
class MainActivity : AppCompatActivity() {
private val navController by lazy { findNavController(R.id.main_nav_host_fragment) }
private val appBarConfiguration by lazy { AppBarConfiguration(navController.graph) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navController = this.findNavController(R.id.main_nav_host_fragment)
NavigationUI.setupActionBarWithNavController(this, navController)
NavigationUI.setupWithNavController(
findViewById<NavigationView>(R.id.navigation_view),
navController
)
}
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.main_nav_host_fragment)
return NavigationUI.navigateUp(navController, appBarConfiguration)
}
}
FragmentToolbar.kt interface
interface FragmentToolbar {
fun clearActionBar()
fun setCustomActionBar()
}
MainFragment.kt
Next class MainFragment.kt is abstract with methods clearActionBar() and setCustomActionBar() that can be overridden. They are defined in FragmentToolbar.kt interface, because if you set custom layout in first fragment, you will see it also in all other fragments. So, you will almost always have to clear your ActionBar and this class is responsible for standard realisation. setCustomActionBar() is up to you\
abstract class MainFragment : Fragment(), FragmentToolbar {
val actionBar: ActionBar? by lazy { (requireActivity() as AppCompatActivity).supportActionBar }
override fun clearActionBar() {
actionBar?.apply {
setDisplayShowCustomEnabled(false)
setDisplayShowTitleEnabled(true)
}
}
override fun setCustomActionBar() {}
}
MyFragment.kt
class MyFragment : MainFragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
clearActionBar() // defined in MainFragment
// Your code
}
override fun setCustomActionBar() {
actionBar?.apply {
setDisplayShowCustomEnabled(true)
setCustomView(R.layout.view_cart_and_bill)
}
actionBar?.customView?.apply { /* Here use findViewById to find necessary views inside your custom layout */ }
}
}
Hope my answer helps you. It also would be great, if ViewBinding could be used with this layout added in runtime, but i have not solved this issue yet
My bottom navigation view with Navigation Component is recreating my fragments each time I navigate. I would like a Singletop, how can I do this?
I followed this tutorial: https://proandroiddev.com/android-navigation-arch-component-a-curious-investigation-3e56e24126e1
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupNavigation()
}
private fun setupNavigation() {
val navController = findNavController(R.id.mainNavigationFragment)
setupActionBarWithNavController(navController)
bottomNavigationView.setupWithNavController(navController)
}
override fun onSupportNavigateUp() =
findNavController(R.id.mainNavigationFragment).navigateUp()
}
I am trying to pass click event on a view in a Fragment to the Activity. I was following documentation guide from here.
As I understand it and I could be wrong but we need to create an interface, add a method declaration to it and trigger the method from when the click event is received on the Fragment. The Activity should then implement the interface defined in the fragment, so that the activity receives that event.
I have my Fragment:
class MoreFragment : Fragment() {
internal var callback: OnMoreItemClickedListener
fun setOnMoreItemClickedListener(callback: OnMoreItemClickedListener) {
this.callback = callback
}
interface OnMoreItemClickedListener {
fun onAddClothClicked()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_more, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
}
override fun onStart() {
super.onStart()
btn_add_clothes.setOnClickListener {
callback.onAddClothClicked()
}
}
}
And I have my Activity:
class MainActivity : AppCompatActivity(), MoreFragment.OnMoreItemClickedListener {
override fun onAddClothClicked() {
Log.d("MainActivity", "HERE")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
val 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.
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_clothes,
R.id.navigation_seasons,
R.id.navigation_notifications,
R.id.navigation_more
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
override fun onAttachFragment(fragment: Fragment) {
super.onAttachFragment(fragment)
if(fragment is MoreFragment){
fragment.setOnMoreItemClickedListener(this)
}
}
}
Now the error that I am getting is:
MoreFragment.kt: (16, 5): Property must be initialized or be abstract
MoreFragment.kt: (16, 5) is internal var callback: OnMoreItemClickedListener.The doc link above does suggest the same/ similar code but it does not work.
Things that I have tried
I have tried putting a lateinit var for the callback: OnMoreItemClickedListener variable there but no luck. As it never gets initialized.
I put internal var callback: OnMoreItemClickedListener? = null and referenced callback using the null safe operator ?. but was not able to get the click event.
I am fairly new to Kotlin and Fragments too so please help me figure how to do this. Thanks
In Fragment override onAttach method
private lateinit var callback: OnMoreItemClickedListener
override onAttach(context : Context){
callback = context as OnMoreItemClickedListener
}
then your callback will be init. as onAttach will be first. so no Property must be initialized or be abstract errror will occur.
also in Activity implement your interface(OnMoreItemClickedListener) and override its method. You will get callback
Your both way is looks OK
I have tried putting a lateinit var for the callback:
I put internal var callback: OnMoreItemClickedListener? =
null
I guess issue is here just debug below code. is it going inside if
if(fragment is MoreFragment){
fragment.setOnMoreItemClickedListener(this)
}