After navigating with popUpTo, the next fragment shows back button in NavBar - android

So I have a standard scenario, described in the Android dev docs.
Both fragments are so far pretty much stock from the New | Activity | Basic Activity wizard. I've only slightly changed the first fragment so far to start working on the login credentials.
Basically, after I complete the login, I want to clear my call stack and just move to the main app screen.
This works to some extent for me, by adding:
app:popUpTo="#id/FirstFragment"
app:popUpToInclusive="true"
and then navigating
NavHostFragment
.findNavController(this)
.navigate(R.id.action_FirstFragment_to_SecondFragment);
It works because when pressing the back button / performing the gesture, the app will simply close.
What DOESN'T work, is that now the title bar for the second fragment is showing the back arrow. The back arrow doesn't even work as there's no stack to pop, but still it's there.
I've investigated answers to this question.
I cannot seem to get the FirstFragmentDirections to build, so I can't try that solution. Tried (unsuccessfully) to modify gradle file and searching for a file that would be built, but not accessible.
I am now left with an arrow that has no place where it's rendered and would appreciate any pointers on how to get rid of it.
Edit1: I have now discovered how to get the FragmentDirections class to be generated. It's described here - all the SO suggestions on the matter lacked the repository inclusion and only mentioned the plugin inclusion.
Edit2: The arrow depends on fragment properties. One of such properties is navigation's app:startDestination. It will start the activity on this fragment, but also display the back arrow on all other fragments.
My task has shifted from total non-understanding to seeking how I can specify / override this per-fragment.

Related

MvvmCross ViewModels not destroyed on FragmentManager.PopBackstackImmediate()

I have built a Xamarin Android app that presents the user with a series of data entry forms, like a wizard. The wizard has a bottom navigation bar with previous and next buttons, and a menu button that when pressed displays a list of all forms in the wizard and allows the user to jump to any given form.
The desired functionality is to preserve the linear navigation, so that when the user jumps to the middle of the wizard, they can still use the previous and next buttons to page through the various forms in order. They should also be able to use the hardware back button to view the previous form in the wizard.
I suspect my implementation is not MvvmCross friendly because I'm seeing some bugs with it, specifically my viewmodels are not destroyed when I clear the fragment backstack (wizard hosted in an Activity, each form is a Fragment).
How should I implement this?
Have you tried using this overload of PopBackStackImmediate? This one will pop all of the fragments until the one you specify in the string (if inclusive flag is passed, then that fragment is also popped) so you spare iterating all over the fragment backstack.
Activity.SupportFragmentManager.PopBackStackImmediate("myViewTag", (int)PopBackStackFlags.Inclusive);
where "myViewTag" is the UniqueImmutableCacheTag of your View
There has to be something unconventional about my implementation, but with a deadline and little help from the community, what do you do to fix your hack, but to hack it more?
My solution was twofold:
1) instead of using FragmentManager.PopBackstackImmediate(), I implemented a while loop with the condition: activity.SupportFragmentManager.BackStackEntryCount > 0, calling Close(fragment.ViewModel) in the body. This should have fixed the bug, but it didn't.
2) The ViewModels I was requesting to Close were still not being disposed, so I had to resolve the current IMvxMultipleViewModelCache and call GetAndClear on it with the expected parameters. This forced my ViewModels to be disposed so they will be recreated on the next viewing of its Fragment.
This feels like a hack. Closing a ViewModel should dispose of it whether it's associated with a Fragment or Activity, but for some reason it wasn't. That's the key to this bug, but like I said, deadline, hack on hack.

Android: Tabbed App with Navigation Stacks

I'm working to clone an iOS app that uses a UITabBarViewController as the "main/outer/root" UIViewController. Each of its tabs has a UINavigationController as the root to manage a navigation stack of UIViewController instances.
I am trying very hard to do 2 things:
Replicate the functionality of the iOS app.
Do so in true "Android" fashion (i.e., not forcing an iOS paradigm in a non-iOS world).
I've searched around a good bit, but this topic seems to have been changed a lot over the past few years. I tried a solution that had a single Activity that managed everything, but that required maintaining separate back stacks, and seemed to get a thumbs down from most users around here.
Currently, I have a solution that is "sort of" working. As I understand it, my current setup is:
A single, BaseActivity class that extends ActionBarActivity.
All activities in my app (only 2 thus far, but slated for 5) extend this class.
This base activity loads a drawer.xml layout, which has an android:support.v4.widget.DrawerLayout as its outermost item.
Inside of this is:
A FrameLayout, which is the container for my fragments.
A ListView, which is the drawer itself.
Based on my testing of the app, this setup gets me most of what I want.
When I "navigate" (using the drawer) to a different activity, the correct activity is loaded.
When I "navigate" to a "deeper" fragment (via on-screen controls), the correct new fragment is pushed and I've overridden onBackPressed() in BaseActivity to correctly pop the back stack if appropriate.
The problem I'm encountering is this:
The app is launched and "Activity 1:Fragment 1" is displayed. I click on an item to move to "Activity 1:Fragment 2" (which works well).
I use the drawer to launch Activity 2, and "Activity 2:Fragment 1" is shown. So far, so good.
I use the drawer to go return to (at least that's my intention) to the existing Activity 1, and "Activity 1:Fragment 1" is displayed.
I've looked into Activity/Intent launch flags, but I'm not sure I really get what's going on. So far, my reading has led me to try the following steps:
When switching Activites, setting its flag like this:
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
In my AndroidManifest.xml, setting Activity launchMode like this:
android:launchMode="singleInstance", or like this
android:launchMode="singleTop"
An odd, additional problem I'm facing is that launching a new Activity doesn't seem to create the Activity back stack like I expect. When I move from Activity 1 to Activity 2 (ignoring the lack of fragment back stack maintenance for a moment), pressing the back button from Activity 2 closes the app, which confuses me.
Is there a single, current best practice for this type of app structure/navigation?
the good approach is to have 1 activity and handle the back stacks manually as Android has only 1 stack. I've done this several times and finally I created a library called Tab Stacker that does the job: a fragment history for each tab.
It is open source and fully documented, and can be included easily with gradle. You can find the library on github: https://github.com/smart-fun/TabStacker
You can also download the sample app to see that the behaviour corresponds to your needs:
https://play.google.com/apps/testing/fr.arnaudguyon.tabstackerapp
If you have any question don't hesitate to drop a mail.

Facing issue with setCustomAnimations() while replacing fragments

I have 2 fragments A and B. I am replacing the fragment B with A, and setting setCustomAnimations(right_to_left, fadein, fadeout, left_to_right). Fragment B is behaving correctly like it is coming from right to left and on pressing Back button and do pop back stack it is leaving from left to right. But no animation happens to the fragment A its doesn't fade in and fade out.
Note: I have tried it with creating the sample application and it works perfectly fine but when I implement the same thing into my project it behaves as above. What mistake am I making while implementing the setCustomAnimation() in the my project?
I have already tried:
have a latest support library
hardwareAccelarated="true" in the manifest file
add setCustomAnimation() before replace() method
compile it with above 3.0
I doubt there can be an issue with the framework my app is built on but not sure. Can anyone please let me know what else I can check to make it work?

Android Ghost Layout on Activity Transition

I noticed something strange today and i'm not sure the best way to describe it.
I have two activities (A and B). I'm on A and start a new intent for B. I override the pending transition to be a slow slide up. This part is fine.
When B is sliding up I noticed that I can still tap on the screen where B's buttons would be and use their actions. (example, there is a close button to close B. When I tap on it's destination area it will close B even though it hasn't completely gone up).
My current solution is to disable all of the buttons until the animation is done. This is working fine, however I'd be interested to know if there is a better (more standard) solution. If anyone can explain (and confirm my suspicions) then that'd be a nice plus!
This is the expected behavior. Android is simply animating a transition and not actually moving the views from one physical location to another.

Android - Temporal & Ancestral Navigation with ViewPager & Fragments

I'm trying to work out the best way to implement the 'ancestral' and 'temporal' navigation (utilising the up button and back buttons), for a music player I'm working on.
Currently, the user is presented with a viewpager, and can page between three main fragments (ArtistMain, AlbumMain and SongMain). Upon choosing an item inside that view, a fragment transaction occurs, and the viewpager goes out of view, replaced by a new fragment (AlbumSub, Songsub or player, depending on where the user came from). The user can then navigate deeper, until a song is chosen, and then they are taken to the 'player' screen.
I guess the question is: How do I implement all of this conditional navigation?
I'm fairly new to android and programming in general, and I just can't seem to come up with an efficient way to achieve this. At the moment, as each fragment is brought into view, the app is checking to see where the user just came from, and then determines where the user should be taken if back or home is called. This means I have a booleans like "fromArtistMain", "fromAlbumSub", and I'm checking for things like "fromSongSub && fromPlayer".. it's all turning into a bit of a mess.
I've drawn a diagram (in paint, sorry!!), to depict the navigation I'm trying to achieve. The yellow represents the 'up' button press, the red is the 'back' button press, and blue is just normal navigation. The green arrows are meant to represent the view paging:
Any advice is welcome. It might take something really simple that I've just overlooked.
Thanks for your time.
Edit:
I have been adding fragments to the backstack, and using popBackStack() calls, the problem is that popping the backstack is not necessarily the correct option in each case.
I've currently got a whole mess of code trying to determine whether a transaction should be added to the backstack in the first place.
Consider the following:
User chooses a song straight from 'SongMain', and is taken to 'Player'. Now using the home button should (in my mind), take the user to SongSub (the list of songs from the album that the chosen song belongs to). Now if the user navigates up again, they will be taken to 'AlbumSub', the list of Albums by that artist. This is a fragment transaction, but adding to the backstack would mean the user would be taken down a level on back press (which I think would be unexpected). So in this case I don't add that particular transaction to the backstack - seems fine, but there are quite a few different cases, and combined with a viewpager at the top which needs to come in and out of visibility, the code gets really messy.
All of this means a whole bunch of conditionals determining where the user came from and which path they took to get there..
Would it be wise to have a bunch of booleans in the host activity which get set depending on where the user has navigated, and then checking those booleans to see if a transaction should be added tot he backstack? This is kind of what I already have, but it seems really inefficient.
What you're looking into is called the Back stack. You can read more about it here at developer.android.com.
If you use a single Activity to host each of these Fragments then you can modify your code to explicity add your Fragments to the Fragment Back stack using code like so:
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.container, fragment);
transaction.addToBackStack("NameOfFragment");
transaction.commit();
So, if you do the above code with Fragments like so:
Fragment1 -> Fragment2 -> Fragment3 -> Fragment4
Then from Fragment4 if you call this method:
getSupportFragmentManager().popBackStackImmediate();
Then Fragment4 will be finished and Fragment3 will restart. Simple. You can have this line called from a button click or you can override the behaviour of the back button to call it.
Please note in the examples I've used the function getSupportFragmentManager() which is a method name in the Compatibility Package. If you're not using the Compatibility Package then you can instead call getFragmentManager().
EDIT
The problem with the navigation you envisage is that breaking out of the backstack paradigm half way through means that your app will "Act Differently" than the rest of the OS. This is by and large discouraged by Google. But then again, saying that, I do exactly the same in my app for very similar reasons :).
When you navigate "up", along one of your yellow lines, you are following a discrete link (so, startActivity(new Intent(this, SongSub)); or whatever) and you want this to "break" the backstack.
It's at this stage you can make a decision about how you want to go forward:
You can start a Task (backstack) using SongSub as 0th item. This is from memory what the Google Music app does and you're right, it's annoying. When you press back it should technically exit the app. Yuk. IMO if you're in an obvious page hierarchy, back should always navigate down the hierarchy over exiting the app.
You can start a new Task using ArtistMain as the 0th element and layer fragments discretely ontop before commiting your transaction, in effect creating a new backstack each time you go "up" rather than "back" (your backstack would now be ArtistMain->ArtistSub->SongSub). This is what I think your trying to ask here. It's possible but it's messy.
You can create a more linear structure (probably the best idea if possible). Ignore the backstack paradigm, make "back" and "up" always go up a level no matter where you came from (Player always goes to SongSub, SongSub always goes to AlbumSub). This will give the user the least confusing and most transparent (as well as easiest to implement) experience - the user will learn quickly how to navigate (and how many "backs" to press) to get to where they want to be.

Categories

Resources