Android Navigation, I can move any fragment which is not connected - android

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.

Related

Component Navigation , pop from backstack with arguments

Let's say I have three fragments, A, B, C;
A -> B <-> C
Between B and C it is a circular relationship. Either B or C fragments requires arguments, example
val args = Bundle()
args.putString("StringKeyBC", argValueBtoC)
findNavController().navigate(R.id.action_fragmentB_to_fragmentC, args, null)
args.putString("StringKeyCB", argValueCtoB)
findNavController().navigate(R.id.action_fragmentC_to_fragmentB, args, null)
The problem is that every time I move between B & C, the fragments are added to back stack and I don't want that. If the fragment is already to back stack I want just to pop it, but if I use popBackStack I can not add arguments anymore:
public boolean popBackStack(#IdRes int destinationId, boolean inclusive)
So, how can I constanlty switch between the two fragments without adding them every time to back stack?
You can pop fragments from back stack simply by adding a popUpTo attribute to a navigation action. This way you navigate using an action with arguments, but with pop back stack behaviour.
For example, you can add attribute app:popUpTo="#+id/fragmentB" to the action action_fragmentC_to_fragmentB. This way you'll be popping fragmentC from backstack each time you go from fragmentC to fragmentB.
See the docs with example for this here.
There's another option, which is likely an overhead for the case you described, but that allows to use popBackStack method and send arguments - using 'navigate back with result' approach. For it fragments should implement an interface (callback) with a method that receives bundle. Use addOnBackStackChangedListener in the fragment manager to trigger this method, providing all the data necessary, after popBackStack is called. (Described here in the section "How to navigate back with a result?": https://medium.com/google-developer-experts/using-navigation-architecture-component-in-a-large-banking-app-ac84936a42c2, and with slightly different implementation here: https://medium.com/#zawadz88/david-vávra-thank-you-for-this-great-article-ae3e602b880a)

Android NavController move to another host destination

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.

Android Jetpack Navigation Pass Lambda/Delegate between Fragments

I would like to pass a lambda from fragment A to fragment B when A transitions to B via a findNavController().navigate(R.id.action_a_to_b). The use case is B helps pick an item out to display on screen A.
Something like:
// In A
findNavController().navigate(R.id.action_a_to_b, configBlock: { fragmentB ->
fragmentB.itemSelectedCallback = this::itemSelected
})
I recognize this pattern doesn't quite fit with what Google is pushing (I assume they want shared observed view models with fragments not communicating between each other) but I am not looking to transition to that architecture style yet.
This is not yet possible, however, there is an existing feature request for being able to navigate for a result, which would let you get this type of functionality.

Recreating backstack with Android Navigation Architecture Component

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, ...)

How to pop back stack for Activity with multiple Fragments?

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...

Categories

Resources