Navigate Directly to Fragment Deep in Screen Hierarchy - android

I am working on an app that has a tab bar navigation pattern. Each tab represents a feature of the application that is backed by a ready-only database that is updated daily. If the data for a feature is not present in the database, I have an AlertDialog that prompts the user to update their database from the updates screen. The UpdatesFragment is a sub screen to the SettingsFragment which is a top-level option on the tab bar.
What is the best way to show the UpdatesFragment directly from the dialog but still show the settings tab as selected and allow the user to navigate back through settings when they press back? Should I go through the SettingsFragment and pass an argument for it to directly replace itself with the UpdatesFragment?
Home | FeatureA | FeatureB | ... | Settings
|
Updates

I'd argue that you should let the user update from any place, or at least, trigger the update from the AlertDialog itself.
Regardless of that, you want to construct your backstack the same way it would have been constructed if the user tapped all the way to the final destination.
Android provides a TaskStackBuilder for the matter, which you can find in the official Android documentation, located here.
UPDATE AFTER YOUR COMMENT:
Hmmm I see what you mean. Well then you'd have to create fragment transactions and/or use Bundle values to tell the "next" fragment that it needs to open the next and so forth. To be honest, when "shortcuts" like these are needed, there's a possible architecture flaw (no offense) that should rather be addressed before the code starts getting more and more spaghetti.
However, since I don't know your code and your app, I won't judge you :)
What I would do if I were facing this situation, is try to go this route:
Fire an intent to the same activity (so your activity receives onNewIntent()) and pass a bundle with the required information for your activity to resolve the intent and fire the correct fragment transactions?
If UpdatesFragment should replace settings fragment, then your activity should do either of these upon receiving that intent:
1) Create a Fragment Transaction to replace the current fragment with SettingsFragment.
2) Pass a Bundle to SettingsFragment telling it to open UpdateFragment (and whatever other information is required).
3) Add that to the Backstack.
4) SettingsFragment should check (upon startup) if the args bundle contains such value and should immediately proceed to create another FragmentTransaction to replace itself with UpdatesFragment (and also pass any args in a Bundle that the former may require to perform its actions).
or…
maybe you can try to do all in one step (I'm not sure if this will work, it depends upon your architecture, but it's easy to try).
PseudoCode (assumes you already have your Fragments instances and such)
fragmentTransaction
.replace(your_container, settingsFragment)
.addToBackStack(null)
.replace(your_container, updateFragment)
.addToBackStack(null)
.commit();

Related

How to remove an underlying fragment programatically?

The scenario is as follows:
"Dashboard Fragment" -> Fragment 1. This transaction is added to back stack. With tag specifed while replacing the fragment as "frag1";
Fragment 1 -> Fragment 2. This transaction is also added to back stack. With tag while performing replace operation. The tag is let us say "frag2".
Now in Frgment 2 when the user wants to save data, it will make the web service call. And if it is successful, I want to replace Fragment 2 with NEW Fragment 1. And if the user presses the cancel button, it will simply perform the back press event and load the Fragment 1.
In the case of successful web service call, replacement is done properly but when I press back button there is still OLDER Fragment 1. It is for obvious reasons.
To get rid of this when the web service call in Fragment 2 is successful, I have written the following code.
if (null != getActivity().getSupportFragmentManager().findFragmentByTag("frag1")) {
getActivity().getSupportFragmentManager().beginTransaction().remove
(getActivity().getSupportFragmentManager().findFragmentByTag("frag1"));
}
loadFragment1();
and then I use the call back method to load the Fragment 1 in the same frame layout and replace Fragment 2.
It goes inside of the if block but the older fragment "Fragment1" is still there- UNHARMED.
I am not using any tag in the method addToBackStack() and keeping it like this addToBackStack(null)
I am not able to remove the older Fragment 1. Please let me know what is wrong with this approach. Thank you.
try this
findViewById(R.id.fragment1).setVisibility(View.GONE);

Proper way to handle the lifecycle of showing and hiding fragments?

In the main part of my application I have 2 fragments open at once. Only one is shown at once though.
The second fragment is being created from the onCreateView method of the first fragment, as the second fragment is used to control the first fragment.
Bundle args = new Bundle();
args.putInt(ReferenceSelectorFragment.ARG_TAB_INDEX, Tab.BOOK.position);
mReferenceSelectorFragment.setArguments(args);
fm.beginTransaction()
.add(container.getId(), mReferenceSelectorFragment, FRAGMENT_TAGS[0])
.hide(mReferenceSelectorFragment)
.commit();
I'm properly hiding and showing the fragments using a fragment transaction:
getActivity().getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.up_in, R.anim.up_out, R.anim.down_in, R.anim.down_out)
.hide(ReaderFragment.this)
.show(mReferenceSelectorFragment)
.addToBackStack(null)
.commit();
This works wonderfully, as expected, except for one tiny flaw. When the application is minimized for a while and the OS closes the process, when the application is opened again, trying to restore the previous state, both fragments are restored on top of each other. Meaning both are seen at the same time on top of each other. (This can be replicated by pressing the home button on the app, and then using DDMS to kill the running process).
I've tried everything to detect this and fix this, but it's proven very difficult to programmatically distinguish between a rotation and the application being restored after the process has been exited. What is the proper way to deal with the lifecycle of showing and hiding fragments?
This might help you out: https://stackoverflow.com/a/13306633/969325
Basically you could pass a null bundle in your activities super.onCreate(savedInstanceState); or only create new fragments when they doesn't already exists. I suspect that is your issue.

Proper Activity switching in Android? How?

I have the default Android project layout in Eclipse. The current one, that comes with a "dummyText" and a switcher on top. (I selected that one during the wizard.)
I want to use the top select bar to switch screens. Between Main, and Settings, and Result.
How do I detect the current activity?
Because.. if I have a switch, like:
switch (getArguments().getInt(ARG_SECTION_NUMBER)) { ... }
It will get into an infinite loop if the current screen is the selected one on the top.
(E.g.: Value 1 = Main screen. And you open the application, and it's value 1. And it's on main screen. It will indefinitely open up the main screen again and again. If you select an other value, like 2, it will go to the proper screen, and it won't loop.)
How am I supposed to fix this?
(I'm opening the other Activity with a new Intent, and then I call startActivityForResult(...).
Update #1:
The switch went into the "DummySectionFragment", which gets created at the onNavigationItemSelected.
Which looks like this:
Fragment fragment = new DummySectionFragment();
Bundle args = new Bundle();
args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, position + 1);
fragment.setArguments(args);
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, fragment).commit();
So basically that looks right... to me. The switch is activated, the corresponding value gets sent to Dummy, and a switch could just work to launch the proper Activity. I just need to write an if statement, that IF the current Activity matches the "to-be-invoked" one, the app should do nothing.
How am I supposed to implement this?
(I know the code is a little messy, blame Google for it's sample.)
In case you are not familiar with the Google example/code I used, here it is:
https://gist.github.com/anonymous/4edaefa42dd1be96e6e4
It's the "Blank Activity" -> "Dropdown" one.
I think you'r not using the sample as intended. One way would be to put the switch in the onNavigationItemSelected and within it launch the correct fragment (instead of DummySectionFragment) according to the selected item.
So this sample is built on Fragments, you should use them for the different sections, instead of launching a new activity.
The other way would be to have the DummySectionFragment use the ARG_SECTION_NUMBER to decide which layout to inflate, and inflate different layouts for different sections. In any case launching a new activity per section is not the way this sample is supposed to work.
Edit: Here are good guides for working with fragments:
http://developer.android.com/training/basics/fragments/index.html
http://developer.android.com/guide/components/fragments.html

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

How to load new fragment in same space as current fragment

I'm looking for some advice on the best way to handle fragments which launch other fragments.
I'm converting an app which I started writing using a more Activity based approach and have since begun moving it over to using Fragments. I have some Fragments which used to launch a new Activity and I want to move them over to launching other Fragments in the same view that the current Fragment is residing.
For example - I have an Activity which has a WebView which uses a WebViewClient to handle internal js->java interactions. My WebViewClient can launch other Activities, which I used to do with :
i = new Intent(context, GoogleMapActivity.class);
startActivity(i);
This webview activity can either be fullscreen or in a view with a menu on the side, but I want the webview to respect the layout - so if the menu is present, it should stay present when launching new Fragments - I just don't know the best approach to writing the code which launches the Fragments.
So...is there a way, within a Fragment, of essentially telling a new Fragment to load in to the same space as the current Fragment or does there need to be some interaction with the Activity?
** EDIT **
Given that there are a few different layouts which could be used, I don't always know which id I should be targeting to put the fragment in - hence I need to know if there's a way to do this without knowing the id (as in the replace method for example).
getFragmentManager().beginTransaction()
.replace(((ViewGroup) getView().getParent()).getId(), fragment)
.addToBackStack(null)
.commit();
This should replace parent container with desired fragment.
That should be doable via FragementManager.replace(). Have a look at the documentation for Fragment and especially the longer example in the "Layout" section there.
If you want to add Fragment rather replace it, use:
getFragmentManager().beginTransaction().add(R.id.fragment_container, new Fragment()).commit();

Categories

Resources