Move to MainActivity with Jetpack Navigation - android

How i can move from SettingsListFragment in my MainActivity?
<fragment
android:id="#+id/settings_list_fragment"
android:name="com.mandarine.targetList.features.settings.SettingsListFragment"
android:label="#string/settings"
tools:layout="#layout/fragment_settings_list">
</fragment>
Because in my navigation graph i haven't screen from MainActivity.
Whole navigation i have in my one activity, u can check code
class MainActivity : AppCompatActivity(), MainActivityViewContract {
private lateinit var auth: FirebaseAuth
private lateinit var mAuthStateListener: FirebaseAuth.AuthStateListener
private val presenter = MainActivityPresenter(contract = this)
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
signIn()
setupViews()
}
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.nav_host_fragment).navigateUp(appBarConfiguration)
}
override fun onResume() {
super.onResume()
auth.addAuthStateListener(mAuthStateListener)
}
override fun onPause() {
super.onPause()
auth.removeAuthStateListener(mAuthStateListener)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
presenter.onActivityResult(requestCode, resultCode)
}
override fun cancelSignIn() {
finish()
}
private fun setupViews() {
val host: NavHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ?: return
val navController = host.navController
appBarConfiguration = AppBarConfiguration(navController.graph)
val drawerLayout: DrawerLayout? = findViewById(R.id.drawer_layout)
appBarConfiguration = AppBarConfiguration(
setOf(R.id.target_list, R.id.settings_list_fragment, R.id.calendar_fragment),
drawerLayout
)
setupActionBar(navController, appBarConfiguration)
setupNavigationMenu(navController)
setupBottomNavMenu(navController)
}
private fun setupNavigationMenu(navController: NavController) {
val sideNavView = findViewById<NavigationView>(R.id.nav_view)
sideNavView?.setupWithNavController(navController)
}
private fun setupActionBar(
navController: NavController,
appBarConfig: AppBarConfiguration
) {
setupActionBarWithNavController(navController, appBarConfig)
}
private fun setupBottomNavMenu(navController: NavController) {
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_navigation_view)
bottomNavigationView?.setupWithNavController(navController)
}
private fun signIn() {
auth = FirebaseAuth.getInstance()
mAuthStateListener = FirebaseAuth.AuthStateListener { firebaseAuth ->
presenter.signIn(activity = this, user = firebaseAuth.currentUser)
}
}
}

You can add an <activity../> tag in your navigation graph and set it as destination in actions.
<activity
android:id="#+id/main_activity_destination"
android:name="com.mandarine.your.package.path.here.MainActivity" />
Then, you can call navController.navigate(R.id.main_activity_destination)
Or, if you want more control over the flow from the nav graph, you can create an action:
<action
android:id="#+id/go_to_main_activity"
app:clearTask="true"
app:destination="#+id/main_activity_destination"
app:launchSingleTop="true"
app:popUpTo="#id/main_activity_destination" />
Then, you can call navController.navigate(R.id.go_to_main_activity)

The question is not completely clear, but it seems like what you are trying to achieve is fragment navigation using NavController. Before answering the question, let me give you a brief understanding of how the new jetpack navigation works.
You will be having a single activity in your application(can have multiple activities as well, but for the sake of explaining, I am sticking with one). In this particular activity, you are having a single NavigationHostFragment, which is responsible for all your app navigation. This NavigationHostFragment will be equivalent to the container for adding Fragments in older way of doing it. The NavigationHostFragment will have a navigation controller, which will be linked to the storyboard. The navigation.xml file which you referred in the question is your storyboard.
The storyboard will contain all the fragments which you need in the navigation and the navigation directions as well. The navigation directions is referred to as actions. Each action in a fragment will be a navigation to a separate fragment.
Hence the navigation has nothing to do with Activity, it is all happening in the NavigationHostFragment(which I hope you already have in place in your Activity's xml file).
With these fundamentals, with two fragments, in which FragmentA is navigating to FragmentB, your navigation xml file will look somewhat like this:
<fragment android:id="#+id/fragmentA" android:name="com.xxx.xxx.FragmentA"
android:label="fragment_a" tools:layout="#layout/fragment_a">
<action android:id="#+id/action_fragmentA_to_fragmentB"
app:destination="#id/fragmentB"/>
</fragment>
<fragment android:id="#+id/fragmentB"
android:name="com.xxx.xxx.FragmentB"
android:label="fragment_b" tools:layout="#layout/fragment_b">
</fragment>
And then in FragmentA code, where you need to perform the navigation, just call:
findNavController().navigate(R.id.action_fragmentA_to_fragmentB)

Related

lateinit property navController has not been initialized

I was following the official developer.android.com code labs when I encounter this issue
In the MainActivity, you should already have code to set up the app bar (also known as action bar) with the nav controller. Make navController a class variable so you can use it in another method.
class MainActivity : AppCompatActivity(R.layout.activity_main) {
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
setupActionBarWithNavController(navController)
}
}
Within the same class, add code to override the onSupportNavigateUp() function. This code will ask the navController to handle navigating up in the app. Otherwise, fall back to back to the superclass implementation (in AppCompatActivity) of handling the Up button.
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp() || super.onSupportNavigateUp()
}
Run the app. The Up button should now work from the FlavorFragment, PickupFragment, and SummaryFragment. As you navigate to previous steps in the order flow, the fragments should show the right flavor and pickup date from the view model.
This is my android studio error

Cannot hide back button on action bar

My application have two top level fragment A and B controlled by a bottom navigation bar and fragment C can be navigated only from fragment B. I don't want the action bar on fragment C shows back button while it cannot be hidden by setDisplayShowHomeEnabled() or setDisplayHomeAsUpEnabled()
How can I hide the back button?
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val navController = findNavController(R.id.navHostFragment)
val appBarConfiguration = AppBarConfiguration(
setOf(R.id.aFragment, R.id.bFragment)
)
setupActionBarWithNavController(navController, appBarConfiguration)
binding.navView.setupWithNavController(navController)
}
}
Try to set home fragment destination
var navHostFragment = supportFragmentManager
.findFragmentById(R.id.container) as NavHostFragment
var mNavController = navHostFragment.findNavController()
override fun onSupportNavigateUp(): Boolean {
return when (mNavController.currentDestination?.id) {
R.id.homeFrg -> {
true
}
else -> mNavController.navigateUp()
}
}

Jump between two navigation graphs

For navigation, I use the Navigation The component I connected to it using the Navigation Extension BottomNavigationView. I tried to switch from one graph to another, but he always showed me an error that he could not find a path where to go. If the way to go from one graph to another
the code where the Navigation Component and bottomNavView are connected
class MainActivity : AppCompatActivity(R.layout.activity_main) {
private lateinit var appBarConfiguration: AppBarConfiguration
private var currentNavController: LiveData<NavController>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
if (savedInstanceState == null) {
initNavigationComponent()
}
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
initNavigationComponent()
}
override fun onSupportNavigateUp(): Boolean {
return currentNavController?.value?.navigateUp(appBarConfiguration) ?: false
|| super.onSupportNavigateUp()
}
private fun initNavigationComponent() {
appBarConfiguration =
AppBarConfiguration(setOf(R.id.auth_fragment, R.id.main_screen_fragment))
setSupportActionBar(toolbar)
val navGraphIds =
listOf(R.navigation.nav_main, R.navigation.nav_catalog, R.navigation.nav_profile)
// Setup the bottom navigation view with a list of navigation graphs
val controller = bottom_nav.setupWithNavController(
navGraphIds = navGraphIds,
fragmentManager = supportFragmentManager,
containerId = R.id.main_nav_fragment,
intent = intent
)
controller.observe(this, { navController ->
setupActionBarWithNavController(navController,appBarConfiguration)
collapsingToolbarLayout.setupWithNavController(
toolbar,
navController,
appBarConfiguration
)
navController.addOnDestinationChangedListener {...} )
currentNavController = controller
}
}
Tried connecting with <include app:graph = "#navigation/myGraph" /> but that doesn't work either
for multi backstack navigation between BottomNavigationView tabs use this sample of google:
Multiple BackStack Navigation

How to go to the previous fragment by back button in action bar? (Kotlin)

I have two fragments and I want to create interaction between them using backward button in action bar. Ideally, I would like the state of previous fragment was saved.
I could find information for only activities.
For fragments I found this
private fun setupBackButton() {
if (activity is AppCompatActivity) {
(activity as AppCompatActivity?)?.supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
}
But it only displays back button, nothing happens by clicking.
EDIT
In the first fragment I call the second like:
val fragment = UserContentFragment()
fragment.setUser(item.user)
if (fragmentManager != null) {
fragmentManager!!
.beginTransaction()
.replace(R.id.main_layout, fragment)
.addToBackStack(null)
.commit()
}
This is my UserContentFragment second fragment:
class UserContentFragment : Fragment() {
private lateinit var user: SearchUser
fun setUser(user: SearchUser) {
this.user = user
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val root = inflater.inflate(R.layout.fragment_user_content, container, false)
val userImage = root.findViewById(R.id.user_img) as ImageView
if (context != null) {
Glide.with(context!!)
.load(user.profile_pic_url)
.circleCrop()
.into(userImage)
}
val userName: TextView = root.findViewById(R.id.user_name)
userName.text = user.full_name
val toolbar: Toolbar = root.findViewById(R.id.toolbar)
toolbar.setNavigationOnClickListener { requireActivity().onBackPressed() }
setupBackButton()
return root
}
private fun setupBackButton() {
if (activity is AppCompatActivity) {
(activity as AppCompatActivity?)?.supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
}
}
And this its .xml file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:background="#color/colorBlack">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/user_title"
android:layout_width="match_parent"
android:layout_height="100dp">
<ImageView
android:id="#+id/user_img"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginStart="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:id="#+id/user_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="16dp"
android:textColor="#color/colorWhite"
android:textSize="22sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/user_img"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</RelativeLayout>
In order to have a response when you hit the Home/up Button, here are a couple of options to solve this:
First Option
In the fragments that you show up the Home/UP button, override onOptionsItemSelected() method and call the activity's onBackPressed() for home button id
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Handle presses on the action bar menu items
when (item.itemId) {
android.R.id.home -> {
activity?.onBackPressed()
return true
}
}
return super.onOptionsItemSelected(item)
}
Side note:
Instead of showing the Home/Up button on the ActionBar using below, and you've to set the boolean value for each fragment that you need to show up the home button using the method below:
private fun setupBackButton() {
if (activity is AppCompatActivity) {
(activity as AppCompatActivity?)?.supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
}
You can instead setup the ActionBar with AppBarConfiguration in onCreate() method of the activity as below:
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
val host: NavHostFragment = supportFragmentManager
.findFragmentById(R.id.my_nav_host_fragment) as NavHostFragment? ?: return
val navController = host.navController
appBarConfiguration = AppBarConfiguration(
setOf(R.id.second_fragment, R.id.third_fragment)) // IDs of fragments you want without the ActionBar home/up button
setupActionBarWithNavController(navController, appBarConfiguration)
}
By doing this, the up button will show up in all fragments, but R.id.second_fragment, R.id.third_fragment and you no longer need to set the setDisplayHomeAsUpEnabled() to each individual fragment to show up the home/up button. But still you need to override onOptionsItemSelected as mentioned above.
Second Option
Which is neater than the first option. First, you've to implement the above side node to allow the NavController auto controls/configure the ActionBar.
So, the past side note is a mandatory part of this option.
Then override onSupportNavigateUp() in the activity which allows NavigationUI to support proper ActionBar up navigation.
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.my_nav_host_fragment).navigateUp(appBarConfiguration)
}
And then override onOptionsItemSelected() in activity to make Have Navigation UI Handle the OptionsMenu/ActionBar item selection
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return item.onNavDestinationSelected(findNavController(R.id.my_nav_host_fragment))
|| super.onOptionsItemSelected(item)
}
I would say that option 2 is neater than 1, because you write all the code in one place (activity) without touching fragments, and also it automatically configure all the fragments, no need to manually setDisplayHomeAsUpEnabled() or activity.onBackPressed() for individual fragments that you want the Home/Up button to show up.
In Kotlin Fragment with Navigation.
First, you add setHasOptionsMenu(true) in onCreate
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
Then override the onOptionsItemSelected and when R.id.home you can control BACK Button
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.getItemId()) {
android.R.id.home ->
findNavController().navigate(R.id.action_FragmentTwo_to_FragmentOne)
}
return true
}
You need to attach a click listener to the toolbar like :
toolbar.setNavigationOnClickListener { requireActivity().onBackPressed() }
class StartActivity : FragmentActivity() {
/**
* The pager widget, which handles animation and allows swiping horizontally to access previous
* and next wizard steps.
*/
private lateinit var mPager: ViewPager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.start_activity)
val loginButton = findViewById<Button>(R.id.login_button)
loginButton.setOnClickListener {
this.didTapLoginButton()
}
}
private fun didTapLoginButton() {
val i = Intent(this, LoginActivity::class.java)
startActivity(i)
}
}
class LoginActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.login_activity)
//actionbar
val actionbar = supportActionBar
//set actionbar title
actionbar!!.title = "New Activity"
//set back button
actionbar.setDisplayHomeAsUpEnabled(true)
actionbar.setDisplayHomeAsUpEnabled(true)
}
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
// it is important function, you need to write this function in which class/activity you want to show back arrow
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return super.onOptionsItemSelected(item)
}
}
Add this in the NavigationClickListener.
FragmentManager fm = getFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
Log.i("MainActivity", "popping backstack");
fm.popBackStack();
} else {
Log.i("MainActivity", "nothing on backstack, calling super");
super.onBackPressed();
}
Zain's Second Option is very good, but I'll show a different way for the part made in onCreate, with usage of DataBinding:
ActivityMainBinding.inflate(layoutInflater).run {
setContentView(root)
setSupportActionBar(toolbar)
(supportFragmentManager.findFragmentById(R.id.my_nav_host_fragment)
as NavHostFragment).navController.let { navController ->
val appBarConfiguration = AppBarConfiguration(navController.graph)
toolbar.setupWithNavController(navController, appBarConfiguration)
}
}
It's a late answer but I hope it will help someone.
I did it in Kotlin and the way I managed to make it work is:
In the MainActivity I overrode the onSupportNavigateUp:
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment_activity_main)
return navController.navigateUp(appBarConfiguration)
|| super.onSupportNavigateUp()
}
And declared the AppBarConfiguration outside the onCreate()

Android onSupportNavigateUp when NavHost is not in MainActivity

I have an activity which holds FragmentContainerView. In the FragmentContainerView I have a NavHostFragment. I want to implement onSupportNavigateUp in the NavHostFragment.
class MenuNavHostFragment : Fragment() {
fun onSupportNavigateUp(): Boolean {
return NavigationUI.navigateUp(navController,null)
}
}
in MainActivity.kt
private lateinit var menuNavHostFragment: MenuNavHostFragment
override fun onSupportNavigateUp(): Boolean {
return menuNavHostFragment.onSupportNavigateUp()
}
in fragment_menu_nav_host
...
tools:context=".View.MenuNavHostFragment">
<fragment
android:id="#+id/fragment_menu_nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
...
When I press the back button, my application destroys when I want it to go back one step in the navigation stack.
How do I solve this?
check Update UI components with NavigationUI
If you want go back, use findNavController.navigateUp() function
replace the mentioned code by the following one, according to your program:
open fun navigateUp (drawerLayout: DrawerLayout, navController: NavController):Boolean {
val navController = this.findNavController(R.id.myNavHostFragment)
return navigateUp(this.drawerLayout, navController)
}

Categories

Resources