Removing a Fragment from a Fragment - android

I have an Activity that creates a Fragment and then this Fragment creates another Fragment:
Activity -> Fragment1 -> Fragment2
I am now in Fragment2 and I'd like to go back to Fragment1 by clicking on a button.
In my OnClickListener of my button I have:
getActivity().getSupportFragmentManager().beginTransaction().remove(fragment2.this).commit();
This brings me to the Activity. Is there actually a way to just remove Fragment2 and go to Fragment1?
Thank you in advance for your time, I could not find any suitable info online!

#AhmedAbidi has a nice insight to your problem and yes, implementing popBackStack properly may solve your problem. But anyway, I would like to suggest a different approach to handle this type of situations.
Write two public functions in your Activity to switch between your fragments.
public void switchToFragment1() {
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, new Fragment1()).commit();
}
public void switchToFragment2() {
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, new Fragment2()).commit();
}
Now from the button click in your Fragment1 you might launch the Fragment2 via,
((YourActivity) getActivity()).switchToFragment2();
And the same thing while switching to Fragment1
((YourActivity) getActivity()).switchToFragment1();

Your question needs more code for clarity.
Is Fragment1 adding Fragment2 via getSupportFragmentManager() or getChildFragmentManager()? Presumably it appears you are doing the former, and if so, you are incorrectly using the Fragment API. Fragments are not supposed to know about each other, per Android's Fragment Documentation:
Often you will want one Fragment to communicate with another, for example to change the content based on a user event. All Fragment-to-Fragment communication is done through the associated Activity. Two Fragments should never communicate directly.
You should therefore implement the appropriate listeners to communicate from Fragment1 to Activity, which can then decide where/when to add Fragment2 -- then properly utilize the back stack functionality of getSupportFragmentManager().addToBackStack()... with getSupportFragmentManager().popBackStack(). See Android Documentation on Back Navigation for Fragments for further explanation.

Related

What Fragments am I hosting and displaying?

Is there a way to know which Fragment is currently displayed in a given <fragment> container of an Activity without keeping track of all the changes via the onAttachFragment callback?
Is it even possible to know which fragments are displayed when fragment transactions can take place when the user presses the back key? In this latter case, i.e. when a Fragment is re-displayed due to a back, the onAttach is not called.
In my experience, the only way to know for sure which fragment is being displayed is to keep track of that carefully yourself.
For example, you could make a variable in your Activity:
Fragment mCurrentDisplayedFragment;
and then whenever the user requests a different fragment do:
mCurrentFragment = (Fragment) userRequestedFragment;
fragmentManager.replace(container, mCurrentFragment, tag);
Then, whenever you needed to do things to the currently displayed fragment, you could triage it by try/catching a cast or with instanceof.
You could also handle the back pressed behavior by overriding that method in the activity:
#Override
public void onBackPressed() {
int stackSize = fragmentManager.getBackStackEntryCount();
// This counts up from the bottom so the most recent fragment is the biggest number/size
backFragId = fragmentManager.getBackStackEntryAt(stackSize);
// Get a handle on the fragment that is about to be popped
mCurrentFragment = fragmentManager.findFragmentById(backFragId);
super.onBackPressed();
}
Also, are you sure that onAttach is not called when a fragment is popped off the stack? I seem to remember that it will be, and you can call through the interface created there (if you have one and the activity implements it) to register the fragment as the current fragment in the activity at the time.
But to directly answer your question, there isn't a built in way to just know what fragment is currently displayed (and there could be more than one!). The implementation details of that are up to you. Hopefully I've given you some ideas of how it could be handled though. You might also find the FragmentManager documentation helpful.
Each time when you add/replace fragment to the container, use tag for it:
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(R.id.container, fragment, tag).commit();
then you can find out the fragment is current visible or not:
Fragment fg = getFragmentManger().findFragmentByTag(tag);
if(fg.isVisible())
//fg is the current visible fragment
Hope this help!

FragmentManager popBackStack doesn't remove fragment

I'm implementing menu navigation using Fragments. So I begin with Home, and then users can navigate to diferent sections and details of each section.
When a user changes section, then I call pop on the fragmentmanager backstack until I reach Home, and then load the new section.
This is all working as expected. But I'm getting this problem:
load a section that calls setHasOptionsMenu(true) on onResume()
loads another section (old section it's suposed to get out of the stack). I see it OK. No menu is shown
leave the application (for example, go to Android Laucher activity) and then when I return, I see the correct section, but it's showing the Menu of the old Fragment.
I've iterated the backstack and printed each fragment, and there it's not the fragment with the menu.
I put a debug mark on the onResume() method (where the setHasOptionsMenu(true) is flagged) and it indeed enters here, so the Fragment it's still somewhere.
I want to know if I'm doing something wrong and how could I solve it, thx
Update:
I'm using this code to load new fragments
fm.beginTransaction()
.add(container, sectionFragment.getFragment())
.addToBackStack(sectionFragment.getFragmentName())
.commit();
And for remove:
private void clearStack(){
int count = fm.getBackStackEntryCount();
while(count > 1){
fm.popBackStack();
count--;
}
}
NOTE 1: I'm using add instead replace because I don't want to loose the state of my fragment when I navigate back from detail section. When I load another different section, then I call clearStack to pop the stack up to 1, and then loads new fragment. At the end, I'm calling executePendingTransactions() to finish to remove the fragments from the transaction.
NOTE 2: I'm seeing that it is entering on my fragment onDestroy() method, so it is suposed to be destroyed. But I don't know why it is getting called again when the Main activity resumes.
I found that the problem was not in the logic of adding and removing fragment of the stack.
The problem was that some of the fragment loaded another fragments inside of it (it had ViewPager component). Then I thought that when the fragment was removed then these fragments were removed too.
This is true ONLY if you use getChildFragmentManager() method. This method MUST be used when loading fragments inside other fragmets. If not, then the fragments are asociated with the fragments activity.
popBackStack will just revert your last FragmentTransaction.
If you use FragmentTransaction.add, popBackStack will just call FragmentTransacetion.remove.
But if you call FragmentTransaction.replace, popBackStack will call FragmentTransaction.remove and FragmentTransaction.add
For your "NOTE 1" :
FragmentTransaction.replace will not change your fragment state.
I found this question, because after calling
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
this code fragmentManager.getFragments().size() returns me the maximum number of fragments, which were in the stack. I checked every fragment on null. And I found that some fragment is null in my case. Maybe it will help someone)
If you are really looking to remove fragments at once then follow:
How to replace Fragments of different types?
Otherwise use replace transaction for fragments to smooth transitiona and hassel free approach, see https://stackoverflow.com/a/23013075/3176433
Also understand Fragment lifecycle,
http://developer.android.com/guide/components/fragments.html
I had a similar problem where the popBackStack() didn't remove my fragment.
However, I noticed that I called the wrong FragmentManager, where I had to call getSupportFragmentMananger() instead of getFragmentManager().
Maybe there is a <fragment> or <androidx.fragment.app.FragmentContainerView> in an activity with android:name="androidx.navigation.fragment.NavHostFragment", app:defaultNavHost="true" and app:navGraph="#navigation/nav_graph".
In this case navigation is held by nav_graph. If you don't want to use NavController and NavHostFragment, maybe you should remove navigation and clean <fragment> tag.

Communicating between fragments in Android

Based on the example from http://developer.android.com/training/basics/fragments/communicating.html I tried to reproduce the communication between two fragments which are sub fragments of a larger fragment.
In the example, AB activity contains A fragment and B fragment. But I am trying to achieve the same but in my case AB Fragment contains A fragment and B fragment.
The problem is the overridden method in the AB Fragment never gets called. Does this not work because the containing component is a Fragment and not a Activity like in the example? Am I missing out something here?
If you are referring to onClick() or some other onSomething() handler, then these always get called in the Activity class, not the fragment. So in the example you linked, the onArticleSelected() must remain in the Activity, even if you have nested fragments.
To pass info on to the fragment, you have a few options. One, you can keep a reference to the fragment within the activity. This might be lost if your activity recreates (settings event for example).
The second and better way would be to tag your fragments, and then use findFragmentByTag.
When you add your fragment (notice the parameter "my_fragment" which is the tag I gave to the fragment):
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, myFragment, "my_fragment").commit();
Or when you replace one fragment with another:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, myFragment, "my_fragment").comit();
Then, when you want to do something in the fragment from within your onArticleSelected of the activity:
Fragment fragment = getSupportFragmentManger().findFragmentByTag("my_fragment");
if (fragment != null) {
fragment.articleSelected(articleId);
}
You can always use an Interface to communicate between fragments. It is the safest way to do so.

Am I using fragments right?

I am a bit confused about what is the ideologically correct way of using fragments.
As the Android Developers states,
A Fragment represents a behavior or a portion of user interface in an
Activity. You can combine multiple fragments in a single activity to
build a multi-pane UI and reuse a fragment in multiple activities. You
can think of a fragment as a modular section of an activity, which has
its own lifecycle, receives its own input events, and which you can
add or remove while the activity is running (sort of like a "sub
activity" that you can reuse in different activities).
And also:
Fragments decompose application functionality and UI into reusable
modules Add multiple fragments to a screen to avoid switching
activities
And my usage of fragments goes the following way:
I have only one main Activity and a whole bunch of fragments. Instead of starting activities, I prefer replacing fragments.
For example, I have FeedsFragment, FriendsFragment, MessagesFragment, and when I select something from sliding menu, my Activity just replaces the main Fragment. If I'm launching a Fragment from other Fragment, I put the previous one in backstack.
Some fragments require the Activity to change the actionbar, an I do it directly by
((MainActivity)getActivity()).setupActionBar();
Currently I don't have any code that supports tablet layouts (as seen in examples on android developers), but I'm planning to add it.
So, is this the right way of doing things? Or am I completely missing something?
As you know fragment has their own lifecycle, and you can use its event when ever you want from the activity lifecyle.
But Fragments lifecycle depens on activity lifecycle So when actvity destroyed, fragments destroyed also.
// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
Yo can use the fragment transaction, to replace them in one activity, I think you use the same way,
i think you arent wrong way and there are no problem to use fragment instead of using different activites.
But you should think about, you realy need to use them in one activty ? If you dont need to show them in one activity you dont need to use fragment.
A fragment must always be embedded in an activity and the fragment's
lifecycle is directly affected by the host activity's lifecycle. For
example, when the activity is paused, so are all fragments in it, and
when the activity is destroyed, so are all fragments. However, while
an activity is running (it is in the resumed lifecycle state), you can
manipulate each fragment independently, such as add or remove them.
When you perform such a fragment transaction, you can also add it to a
back stack that's managed by the activity—each back stack entry in the
activity is a record of the fragment transaction that occurred. The
back stack allows the user to reverse a fragment transaction (navigate
backwards), by pressing the Back button.
As a result , it is not wrong , but i think if you dont need, creating different activity is easy to maintain.

Android: correct way of jumping between fragments

This is a design question, rather than a technical one.
General case: I want an UI event in a Fragment to make Activity-wide changes.
Specific case: I have two fragments, hosted in the same activity. When the user clicks a button in one of those fragments, I want it to be replaced by the other.
I don't want, however, my Fragments touching my activity. I may want to change the behavior later (maybe, in a bigger screen, show both fragments instead of replacing the first), and I don't want my Fragment code to have that logic.
What I did was implement a Listener class in my fragments, that reports events back to the Activity. This way, if I want to use another Activity class with different display behavior, I can just change the listener and leave the Fragment code untouched.
Is this a good way to go about it? Is there a standard good practice, or a better design pattern?
Using listeners is the recommended way of communicating between Fragment and your activity.
See this Android documentatin section for infromation. Long story short they just implement a listener interface by the Activity class and cast getActivity() result in a fragment to a listener.
From my personal experience this is very convenient because lets you to:
Easilly switch underlying activity (e.g. you host entire fragment in a wrapper activity for compatibility in pre-3.0 and host this fragment along with others in 11+)
Easilly control if the wrapper activity supports callbacks or not. Just check is it does implement the listener and do your app specific actions if it doesn't.
You are right on about using a Listener. This is something I also had to deal with in a project at work. The best way to handle it is to make the Fragment stand-alone in nature. Anything wishing to interact with the Fragment should use its public API and/or set listeners for specific events. If you are familiar with Design Patterns, this is the Observer pattern. The events can be general or specific as well as contain data or no data.
As an example of my project, I had two Fragments. A ListFragment and an InfoFragment that displayed the selected ListItem. The ListFragment already has a Listener interface for my Activity to hook into, but the InfoFragment does not since its your basic Fragment. I added a Listener interface to the InfoFragment that would be notified when the Fragment wanted to close. For the Fragment, this could be by a button press, or specific action occured, but as far as my Activity is concerned, when the Event is triggered, it would close up the Fragment view.
Don't be afraid to use a lot of Listeners for Fragments, but also try to group them by a specific action using data parameters to individualize them. Hope this helps!
A technical answer for:
I have two fragments, hosted in the same activity. When the user clicks a button in one of those fragments, I want it to be replaced by the other.
FragmentTransaction ft = this.getFragmentManager().beginTransaction();
Fragment mFragment = Fragment.instantiate(this.Activity(), Fragment2.class.getName());
ft.replace(android.R.id.content, mFragment);
ft.commit();
public class Example_3_Mainfile extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.example_3_mainfile);
Fragment fr ;//make class that extend to thefragment
fr = new Act_2_1();
FragmentManager fm = getFragmentManager();
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.replace(R.id.fragment_place, fr);
//id get of fragment tag from xml file there decelar
fragmentTransaction.commit();
}
}

Categories

Resources