Is it possible to partially transition to Android Nav Component - android

I'm trying to understand if it is possible to refactor an app, activity by activity to use the new nav component. I can see that it does support multiple activity design but only by creating a nav component for each one. That would mean the effort would need to be across the whole app in one sweep, which is not feasible.
Is there any other way?
I've encountered several problems navigating between activities that do not use the nav component and activities that do, such as how to pop back to a previous non nav activity from a nav activity using an up arrow or even displaying the up arrow in the home fragment of the second activity.
I tried to manually setDisplayHomeAsUpEnabled(true) and declare the parent activity in the Manifest but not only does that solution seem incredibly inaccurate (what if 2 different activities could potentially be parents?) but also forced me to add SINGLE_TOP attribute in order to return to previous state, in total seems like too many bandaids that will surely hit more blocks very soon...
Any suggestions are welcome.
Thanks!

So after about 2 days of searching and trying things I've found that the best option is to setup the toolbar with the nav component but also replacing the appbar config that uses the nav graph with a default builder - one which has an empty top level fragment list. This change essentially tells the nav component there is no top level fragment so always display the up arrow (at least thats my understanding). Also need to add an up arrow listener to pop the back stack to the previous activity if we detect an event at the start destination (entry fragment)
There is no need to make any manifest changes or set the appbarhome attributes which I found conflict with the Nav component behaviour..
Blockquote
private fun setToolbar() {
navController = findNavController(R.id.navigation_host_fragment)
setSupportActionBar(binding.toolbar)
binding.toolbar.setupWithNavController(
navController,
AppBarConfiguration.Builder().build() // Use Builder for empty top level list, up arrow display for home fragment.
)
binding.toolbar.setNavigationOnClickListener {
if (navController.currentDestination?.id == navController.graph.startDestination) {
onBackPressed()
} else {
navController.navigateUp()
}
}
}
Blockquote
Hope this helps others that ran into the same difficulties transitioning to using the new'ish Nav component!!!

Related

Navigation Components addOnDestinationChangedListener inspect stack and get count

The common way to setup a bottom nav to show or hide is with addOnDestinationChangedListener as shown in the docs here:
https://developer.android.com/guide/navigation/navigation-ui#listen_for_navigation_events
A problem I have encountered is the above logic says anything X id we should show the bottom navigation. But I want to allow the same fragment be open via a deeplink or from deep within a bottom navigation without the bottom navigation showing. So how I would think to do this is only show the bottom nav if the number of destinations in the stack are 1 or 2.
There is no way to get the stack count correctly from addOnDestinationChangedListener that I have found. So it seems explicitly Nav Components tries to hide the stack from access outside of the library. So navControlle.backstack is set as a library function.
Below are the things I have tried:
destination.graph with parent but it seems nav components only ever has parent and current nav graph and parent?.parent does not seem to return data.
navController hides the stack so the only way is to access previousBackStackEntry or currentBackStackEntry which does not give the previous previous which is what I need.
supportFragmentManager from activity with supportFragmentManger?.findFragmentById(
R.id.nav_host_fragment
)?.childFragmentManager?.backStackEntryCount
It does not work because the above does get updated in time for destinationChangeListener so it's not accurate. It seems to be +-1 of what it should be because of race conditions.
navHostFrag.childFragmentManager.addOnBackStackChangedListener instead of addOnDestinationChangedListener but the problem here is this is not called on every navigation destination change.
Anyone able to inspect the stack via navigation components from addOnDestinationChangedListener

Using the Navigation component with multiple activities

In the Android docs, it states:
The Navigation component is designed for apps that have one main activity with multiple fragment destinations. The main activity is associated with a navigation graph and contains a NavHostFragment that is responsible for swapping destinations as needed. In an app with multiple activity destinations, each activity has its own navigation graph.
Does this mean that you cannot use the Navigation component to navigate from one activity to another? That appears to be the case.
Second question: If I create an app that uses the navigation drawer, the default code that created when you add an activity that is to have a navigation drawer already has code for managing navigation from one drawer item to another. So is the Navigation component also kind of useless here?
Does Google want us to be creating only single activity apps?
Does Google want us to be creating only single activity apps?
Single activity architecture is something you can move towards. It is not restricted (just recommended) by Google. The architecture has its own advantages and drawbacks. You don’t have to tear up your entire app simply to add Navigation Component. Evaluate and Decide whether it’s worth the pain.
Does this mean that you cannot use the Navigation component to
navigate from one activity to another
No, You can use Navigation Component to replace startActivity calls. Simply add your Second Activity Nav Graph to the First Activity Nav Graph and use the nav controller to navigate between the two.
findNavController().navigate(directions)
Here is a migration guide for it https://developer.android.com/guide/navigation/navigation-migrate#add
In cases, where you want to use a different activity, you can evaluate whether you need a different activity or a different task.
If I create an app that uses the navigation drawer, the default code
that created when you add an activity that is to have a navigation
drawer already has code for managing navigation from one drawer item
to another. So is the Navigation component also kind of useless here?
or
instead of using the default code for a navigation drawer to build
your own navigation drawer that is more inline with the Navigation
component
The thing is you don’t have to build a custom component or anything complicated. Actually, use of the Navigation Component (with the help of NavigationUI class) simplifies the code for the drawer layout and its listeners.
At this link, the documentation helps you implement the Navigation component when using Navigation Drawer and Bottom Navigation View.
With regard to the generated templates, those are outdated and need an upgrade.
References:
https://developer.android.com/guide/navigation/navigation-migrate
https://developer.android.com/guide/navigation/navigation-ui
Short Answer is Unnecessary because:
In Navigation Component idea, you need to have 1 + 3 parts and unlimited fragments.
You can watch Google Navigation Component Video.
Only one Activity.
Single Activity
These are working in the one Activity (Single Activity).
Navigation graph
NavHostFragment
NavController
Why Unnecessary? Because, all parts of "1 + 3" connected with each other.
Details:
Navigation graph is connected with NavFostFragment. Moreover, NavFostFragment defines in XML file of Single Activity. Also, NavController defines by NavController as "navHostFragment.navController".
However, if you really really want to use Navigation Compenent for Activities, you need to use add fragments in Activities.
For Example:
[Activity_A + Fragment_A] and [Activity_B + Fragment_B]
The idea of solution is:
For Activity_A to Activity_B: Navigate Fragment_A -> Activity_B
OR
You can migrate. For Activity_A to Activity_B: Navigate Fragment_A -> Activity_B
More detail: Migrate to the Navigation component by Google

android - How to implement navigation with bottom app bar

I have a project with the following 4 layouts:
I have actually 1 activity holding a bottom app bar and the NavHostFragment where the fragments get injected. The Main Fragment is the home view. There is a Management and a Setting fragment, both are top-level views like the home view but doesn't depend on each other. These 3 fragments can be switched by clicking an item in the nav drawer. For simplification, I'm trying the new navigation architecture component.
Now I have some design questions:
Should I move the bottom app bar into the fragments, cause they don't depend on each other and the FAB button has another action, otherwise I had to change the onClickListener in the activity when the fragments switch?
1.1 Or should I even show the bottom app bar in the management fragment? Maybe just the top bar with the Up caret.
1.2 Or bottom app bar + top bar and Up caret
1.3 and what about the drawer icon, should I display it in the Mgmt fragment
Should I use a fragment or an activity for the Settings fragment? When I use a fragment, I have to move the bottom app bar into the fragments. Otherwise, the bottom app bar would be visible in the Settings Fragment
The Management Fragment has just a recycler view. Clicking on an item should open a DetailView. Should I use a fragment or an activity here?
I read the doc about the navigation architecture component and there is a section about customizing the destination. Also, ich checked the source code and know that the fragments get replaced.
Further, I checked out some frequently used Apps how they implemented the navigation with a nav drawer and noticed, they all replace their fragments. Why does no one hide/show the fragments, is there a reason not to doing this?
Assume we have a fragment with a listview that holds data collected from a database or another expensive task. So wouldn't it be better to show/ hide these fragments instead of replacing it?
Sorry, it's my first app and I'm really confused about this topic, and it seems there are no official recommendations about it out there not even Material Design guidelines don't really make a reference about it.
How would you do it?
setupWithNavController on a Toolbar (or subclasses like BottomAppBar) only set up the Up icon and the title - they do not hook up menu items added to the Toolbar.
As per the Tie destinations to menu items documentation, you must set up your own listener and call onNavDestinationSelected(). For a BottomAppBar, that would be done by setting a Toolbar.OnMenuItemClickListener:
val navController = Navigation.findNavController(this, R.id.navigation_fragment)
myBottomBar.replaceMenu(R.menu.menu_with_nav_item)
myBottomBar.setupWithNavController(navController)
// Connect MenuItems to the NavController
myBottomBar.setOnMenuItemClickListener { menuItem ->
menuItem.onNavDestinationSelected(navController)
}

Why does the android jetpack NavigationUI navigateUp method only open the draw menu if you are at the start destination of the nav graph

When using a NavigationView with a DrawLayout and an ActionBar, jetpack provides some powerful convenience methods to hook everything up together so that these items are easier to implement.
The method setupActionBarWithNavController provided in the NavigationUi library is an extension method for activities that adds a hamburger button to open your drawer and if you override the onSupportNavigateUp:Boolean method on your activity to call navigateUp(drawLayout:DrawLayout, navController:NavController) method, that method will change the hamburger button to a back button and back automatically with a fancy animation, which is super cool.
However it seems that this method is implemented as follows:
public static boolean navigateUp(#Nullable DrawerLayout drawerLayout,
#NonNull NavController navController) {
if (drawerLayout != null && navController.getCurrentDestination().getId()
== navController.getGraph().getStartDestination()) {
drawerLayout.openDrawer(GravityCompat.START);
return true;
} else {
return navController.navigateUp();
}
}
As you can see here, this method has the basic logic of:
if you are not at the start destination of the navgraph, then the button is a back button, otherwise its a hamburger button
This means that only the start destination can open the draw menu via an actionbar button, while all the other destinations have a back button instead, and must swipe to open the draw menu.
Why is this the case? It seems this is a conscious design decision by the android team. Is it frowned upon to have a hamburger button available on multiple destinations?
I would like to have a few main branches that have a hamburger menu and all the screens that branch of from those to have a back button. Is there a way to implement multiple NavGraphs and link them together in one NavigationView?
u_u
I reported a problem I had with the use of a Toolbar inside a fragment and the NavigationUI helper in bug 109868820.
The Googler that helped me indicated clearly (article #7):
[...] the discussions with the material design team has made it clear that the navigation drawer is a global navigation pattern that should be available everywhere
So the material design drawer has to be available on every screen, including the deeper ones when the navigation button is the "up" arrow (not the hamburger). In which case, the drawer is only available from a swipe gesture, as the navigation button navigates up in the app stack.
Indeed (#4 in bug):
You'll still get the title set from the android:label in your navigation graph and proper behavior when it comes to the Up button (particularly important if you're using the DrawerLayout versions)
So the behavior that you found in the source is indeed the "proper" behavior.
Hence:
top level destination: drawer opened through swipe and hamburger button
non-top level destination: drawer opened through swipe only, button navigates up (no hamburger)
In other words, the hamburger button should only be used on top-level destinations.

Should Fragments Be Used In the Back Stack

I know I can use a Fragment and have it added to the back stack, like so:
Fragment fragment = MyFragment.newInstance();
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_holder, fragment).addToBackStack("my_fragment").commit();
Then I can handle changes in onBackStackChanged:
#Override
public void onBackStackChanged() {
boolean hasBackStack = getSupportFragmentManager().getBackStackEntryCount() > 0;
handleBackStackChanges(hasBackStack);
}
But the nagging question I have is whether or not I should.
The initial reason for this question is because of my desire to implement a persistant navigation drawer. One that is available at any point within the application, even if the navigation drawer icon (hamburger icon) is not present, with a swipe from the left edge of the screen.
This leaves me with two options; first I can have a single Activity that has a navigation drawer and hosts all of the Fragments, or I can have multiple Activities that each contain a navigation drawer.
Initially I was drawn to the first option, considering the navigation drawer sample uses fragments for each drawer option. This method would also allow me to easily implement the navigation drawer icon animation in onBackStackChanged:
public void handleBackStackChanges(boolean hasBackStack) {
ObjectAnimator.ofFloat(mDrawerArrow, "progress", hasBackStack ? 0 : 1).start();
...
}
But the sample is essentially a "flat" application, it doesn't handle any back stack changes (except for back to the MainActivity, which doesn't have a navigation drawer). And upon further development I have come across some difficulties with using a single Activity. Especially when handling orientation changes, and changes within the Toolbar.
So now I am thinking it may be simpler to use multiple Activities. Every Activity would extend a custom NavigationDrawerActivity. The MainAcitivity would have a Fragment for each navigation drawer item. Then each subsequent Activity (called from one of the Fragments in the MainActivity) would have a NavigationDrawer of its own, with a reference to which navigation drawer item the original Activity was in.
I can see this route presenting its own problems as well. Handling the navigation drawer icon animation, and handling the back stack when an item is selected from the drawer.
My question is essentially this: has anyone faced these problems already, and is there a more concrete solution? Are there issues (performance related or otherwise) that I could run into when choosing one option over the other?

Categories

Resources