I am trying to implement navigation to specific Detail pages of my app using PendingIntent from a notification, however I am having problems recreating the backstack from the Detail page all the way back to the start destination.
I made a sample app here with a single activity and three fragments to demo this:
Fragment 1 -> Fragment 2 -> Fragment 3
(start dest) <- <-
From Fragment 1 (the start destination), I navigate directly to Fragment 3 using
Navigation.findNavController(this, R.id.nav_host_fragment).navigate(R.id.fragment2,
null,
NavOptions.Builder()
.build())
From Fragment 3, when I call Navigation.findNavController(this,R.id.nav_host_fragment).navigateUp() I am navigated back to Fragment 1. Is there a way to get this to navigate to a newly created Fragment 2 instead?
Thanks to M.G for pointing to the right direction, I have managed to solve this by manually creating the backstack using the navigation library. This means sequentially calling findNavController(...).navigate(...) multiple times to create a backstack.
For example when I deep link to fragment 3 but want an up navigation back to fragments 1 and 2, I call:
findNavController.navigate(R.id.fragment1, ...)
findNavController.navigate(R.id.fragment2, ...)
findNavController.navigate(R.id.fragment3, ...)
Related
My project
Single activity pattern with fragments in kotlin.
Navigation component + bottom navigation view together.
There are four tabs(fragments) in bottom navigation view.
My issue is changing each tab in bottom navigation, then each fragment is re-created which due to the app is laggy.
So my target is making only one instance of each fragment there.
What I tried is:
adding app:launchSingleTop="true" for the tab fragment in grap.xml. DOESN'T WORK.
This idea is if the tab fragment can be pop backed then use it directly or create new. But this only works sometimes. Some times the tab fragment does not re-created but some times are!
I think the reason is pop back stack clear it for some time? Not sure.
binding.bottomNavigationView.setOnItemSelectedListener { item: MenuItem ->
if (!navController.popBackStack(item.itemId, false)) {
NavigationUI.onNavDestinationSelected(item , navController)
}
true
}
I used navController.navigate(item.itemId, null, NavOptions.Builder().setPopUpTo(item.itemId, false).build()) to replace NavigationUI.onNavDestinationSelected(item , navController), still doesn't work.
Any idea? thanks!
just add this piece of code to avoid recreation
binding.bottomNavigationView.setOnItemReselectedListener { }
I have a question regarding to NavController:
There is the scheme (sorry for my painting, hope it helps :D)
I have MainActivity and BottomNavigationView with 3 tabs: A, B, C.
When I tap to A it opens Fragment A1 and there is next button which opens Fragment A2.
In Fragment A2 there are buttons back, next, no problems with navigation here.
The problem is when I need to navigate from Fragment A2 to section B the same like a click on B in BottomNavigationView.
The problem is that it's different graph, how to switch them?
My ideas:
I found work-around: requireActivity().bottomBar.selectedItemId = R.id.graph_b but it's not good idea.
I would like to achieve it using navigation component. I was trying to do findNavController().navigate(R.id.graph_b), but it leads to crash:
java.lang.IllegalArgumentException: navigation destination
com.my.app.staging:id/graph_b is unknown to this NavController
How to make it using NavController ?
There is Google Example project with all architecture:
https://github.com/googlesamples/android-architecture-components/tree/master/NavigationAdvancedSample
And to simplify my question I've added a button in this project, where on click should opens different screen:
You graphs are defined in your Parent Activity and thats where you will be able to control them.
Your first way is actually the solution. Your graphs are defined within your activity. When you are inside any destinations inside your graph A (say A1, A2 etc.) they have no knowledge of the your other graphs B & C. The only way to get to the graph is through parent activity, and hence
requireActivity().bottomBar.selectedItemId = R.id.graph_b
The second way that you have tried will definitely not work because findNavController().navigate(R.id.graph_b) is used when you have nested navigation. In other words graph_b should be inside graph_a which is not your case.
That being said what you can do is just write
requireActivity().bottomBar.selectedItemId = R.id.graph_b
fancier. Instead of running inside your fragments, its better to run inside your activity.
// In your fragment
requireActivity?.moveToGraphB()
// and in your activity
fun moveToGraphB() {
bottomBar.selectedItemId = R.id.graph_b
}
Or more more fancier would be using SharedViewModel which I don't think is necessary.
In graph B you can add the line
where nav_graphA is the name of the graph you want to navigate to when the button is clicked. Then you can add
` <fragment
android:id="#+id/aboutFragment"
android:name="com.mycompany.aboutFragment"
tools:layout="#layout/fragment_about"
android:label="About" >
<action
android:id="#+id/action_aboutFragment_to_nav_graphA"
app:destination="#id/nav_graphA" />
</fragment>
`
to create the action to navigate when the button is clicked.
I am trying to use the Navigation architecture component in my toy app.
First I drew the fragments relationship in my "nav_graph.xml".
For example, I drew 3 fragments A, B, and C like below:
A -> B -> C
So I have 2 actions:
action_a_to_b
action_b_to_c
In general, I use the below code to move another fragment.
In A fragment,
findNavController().navigate(ADirections.actionAToB())
In B fragment,
findNavController().navigate(ADirections.actionBToC())
But you may know, there is another way to navigate.
The fragment id can be used to navigate directly like below:
findNavController().navigate(R.id.a)
In my case, I don't have the action for A to C fragment.
But if I use the below code in my A fragment, I can navigate!
findNavController().navigate(R.id.c)
Is it a bug? or intented?
This is intentional as per the documentation for navigate():
This supports both navigating via an action and directly navigating to a destination.
If you're using Safe Args, then only actions are supported. This ensures that you're only using the connections you've specified in your graph.
I have what I think is a relatively simple problem. I got just one activity with three fragments in it (showing only one at a given time). So, these are my fragments
ORDERS (shows a list of orders)
ORDER DETAILS
NEW ORDER (shows a form to create a new order)
OK so, I guess we all know what the navigation between these should be. I'm having some problems with back navigation though. Here's what happens.
1 -> 3 -> 2 [<] nothing happens [<] goes back to android :/
*[<] = [user presses back key].
I've been doing some research and I know there are some methods I should be using, among them:
.add
.replace
transaction.addToBackStack(null)
(I don't quite understand what that last one is doing but it seems to save the transaction so I can get back, which would be the same ass .add?).
Thanks and leave any comments below I can add any relevant information should it be needed.
The difference between add() and replace() is that replace() removes the fragment in the container and then add the new fragment into it while add() just adding. If you want to navigate between fragments, you should use addToBackStack().
Assume I have an Activity which contains two FrameLayouts (let's call them FrameA and FrameB) which in turn each contain a Fragment (let's call them FragmentA1 and FragmentB1 respectively). Now, I commit a series of individual fragment transactions using code similar to the following...
getFragmentManager()
.beginTransaction()
.replace(frameId, fragment)
.addToBackStack(null)
.commit();
... such that I replace FragmentA1 in FrameA with FragmentA2, then I replace FragmentB1 in FrameB with FragmentB2, then I replace FragmentA2 in FrameA with FragmentA3, then I replace FragmentB2 in Frame2 with FragmentB3, and the final state looks like the picture above (where only FragmentA3 and FragmentB3 are visible).
If I understood correctly how the back stack works, pressing 'back' will interleave popping of the Fragments between FrameA and FrameB (reflecting how I added them).
Does anyone know if it is possible to pop the last transaction on FrameA or FrameB selectively? (i.e. if I pressed 'Pop FrameA' then FrameA would be transitioned back from FragmentA3 to FragmentA2 and, instead, if I pressed 'Pop FrameB' then FrameB would be transitioned back from FragmentB3 to FragmentB2)
Supplement: I know I can get the Fragment last added to a given FrameLayout using the FragmentManager.findFragmentById(int framelayoutId) method, but calling FragmentTransaction.remove(fragment).commit() only removes the Fragment from the View and does not transition the View back to the Fragment it previously displayed.
Basically, no, there is only one back stack for an activity.
You will just need to implement your own separate back stacks.
As of Android 4.0 (and the associated support library) there are APIs that should make this relatively easy -- FragmentTransaction.detach(Fragment) lets you put a fragment into the same state it is when in the back stack, and FragmentManager.saveFragmentInstanceState(Fragment) lets you go further and completely throw away the Fragment object. Not coincidentally, these are used to implement ViewPager's FragmentPagerAdapter and FragmentStatePagerAdapter, respectively, so you could look at the code for these as an example of how to use them.
FragmentManager.popBackStack(String name, FragmentManager.POP_BACK_STACK_INCLUSIVE)
Here is the simplest answer, and the explanation is very clear: Well there are a few ways to go about this depending on the intended behavior, but this link should give you all the best solutions and not surprisingly is from Dianne Hackborn...