Android. How to avoid fragment be recreated when back from another fragment? - android

I'm using MVP in my project. I have an activity. Inside this activity I'm showing fragment. Fragment contains some data, edit text, buttons etc. From this fragment it is possible to navigate to another fragment. Here is my code for showing another fragment:
getParentFragmentManager()
.beginTransaction()
.replace(R.id.main_container, secondFragment)
.addToBackStack(null)
.commitAllowingStateLoss();
Next, when I try to go back from fragment 2 to fragment 1, my fragment one is re-created.The method onViewCreated() is called again with saveedInstanceState = null. Also in the onViewCreated() I call presenter.onCreate(savedInstanceState) and because of this, all requests that should be called when I first enter the fragment are re-called when I back to first fragment.
As far as I understand, when calling a .replace(container, fragment), I cannot avoid recreating the fragment view, but in this case, how can I save all my data in the presenter so that I do not re-execute all requests when returning to the fragment?
P.S. With .add() fragment is not recreated. But in my activity I have toolbar with title, and I don't need it to show in my second fragment. When I open my second fragment with .replace() toolbar is not showing, but when open with .add() toolbar is showing.

Use Fragment Result API :
For implemention this method set - setFragmentResultListener - in source fragment and also set - setResult() and finish() - in destination fragment
Note 1 : for having clean code i suggest you to use FragmentTransaction class in:
https://github.com/mahditavakoli1312/FragmentTransaction---mahdi-tavakoli
Note 2 : use Navigation for navigate betwin fragments and activities Instead tranastion

Related

Previous fragment edittext focus issue

I am using multiple fragments.
I am adding fragment over fragment as below
supportfragmentmanager
.beginTransaction()
.add(R.id.container_login, newFragment, newFragment.javaClass.simpleName)
.addToBackStack(newFragment.javaClass.simpleName)
.commitAllowingStateLoss()
Now the issue is, despite of adding new fragment, previous fragment is not losing focus.
Typing in edittext of current fragment types in previous fragment edditext.
Even action next also loses focus in current fragment and moves cursor in previous fragment.
Kindly help.
The fragment does not lose focus because you use .add method which adds the new fragment over the one which already exists in the container.
Use .replace() method which replaces the existing fragment from the container. This is similar to calling remove(Fragment) and then use .add() method.
If you want to add a fragment in one container is impossible but if you want to replace the fragment with another fragment is possible to do it, because one layout is for 1 fragment, not more than one, so you just need to replace the previous fragment with another fragment
I have a simple code if you want to replace the previous fragment with the new fragment
First, you need to add one method with the parameter of the fragment
fun openFragment(fragment: Fragment?) {
val transaction: FragmentTransaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.container_content, fragment!!)
transaction.commit()
}
If you want to replace the fragment with another fragment just call the method like this openFragment(FragmentClass.newinstance()) soo the previous fragment will be replaced with the new fragment
I hope this code can help you to solve your problem

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.

How to handle fragments backstack changes?

I have some king of header in my activity, which says what kind of fragment is opened now. It's ok, when I'm just replacing one fragment by another, but I have a problem with handling backstack changes in onBackPressed. That's a part of my code in onBackPressed method:
Fragment fragment = fragmentManager.findFragmentById(R.id.main_fragment);
fragmentManager.popBackStack();
fragment = fragmentManager.findFragmentById(R.id.main_fragment);
in first row, fragment=FormFragment{41f01d58 #3 id=0x7f05005f}, and after calling popBackStack I have fragment=FormFragment{41f01d58 #3 id=0x7f05005f} again (but it should be another fragment, even not FormFragment instance).
Is there any way how to find out what fragment is popped from backstack after calling popBackStack?
First of all, usually you don't have to pop the fragment back stack yourself. If your activity is a FragmentActivity, its default onBackPressed() will do the work for you.
To update your header when the fragment is popped from the back stack, put the header update code in the fragment's onResume().

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.

Call Fragments onCreateView in context with Tabs

I have 2 Tabs and 2 Corresponding Fragments. On calling the LAUNCH Activity both Tabs were added and then the first one added will be shown. Thus the first Fragments onCreateView is called the second Fragments ones not.
In my case this is an issue because the first Fragment has to call methods on the second Fragment. Inside the second Fragment there is an Objectreference which will be set by calling the onCreateView in the second Fragment.
Therefore I used following code snippet to solve this
actionBar.setSelectedNavigationItem(1);
actionBar.setSelectedNavigationItem(0);
It works but in my opinion there must be another possibility to solve this issue. Like calling the onCreateView of the second Fragment?
Here is the relevant code snippet. The listener is implemented as in android-dev Sample only with small changes not affecting my issue.
simplexFragment corresponds to the first Fragment
graphicFragment corresponds to the second Fragment
// adds two tabs
actionBar.addTab(actionBar.newTab().setText("Input").setTabListener(new TabListener(null, "input_fragment")));
graphicFragment = new GraphicFragment();
actionBar.addTab(actionBar.newTab().setText("Graphic").setTabListener(new TabListener(graphicFragment, "graphic_fragment")));
simplexFragment.setGraphics(graphicFragment); // sets the internal reference!
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// selects the Tab so the GraphicFragments onCreateView will be called
actionBar.setSelectedNavigationItem(1);
actionBar.setSelectedNavigationItem(0);
Thanks for support.
In my case this is an issue because the first Fragment has to call methods on the second Fragment.
This is not how Fragments are meant to work. The idea is a Fragment should be self-contained and re-usable and one Fragment shouldn't know that another exists (or at least shouldn't rely the existence any other Fragment).
For example, suppose you have 3 Fragments and ActivityA uses FragmentA and FragmentB but you have another Activity (ActivityB) which uses FragmentA and FragmentC. In that case, FragmentA doesn't know what the other Fragment is (B or C) and shouldn't even expect there to be another Fragment at all.
The way to achieve what you want is to use callback methods on the Activity and have it perform the actions on any other Fragments (if they exist). It can do this by either calling public methods on the other Fragments or by passing data in the 'arguments' Bundle when it creates the other Fragments.

Categories

Resources