For unknown reason sometime my fragment is showing menu items that belongs to a different fragment.
I have a single activity with custom back stack to maintain the correct flow using:
Map<Integer, List<Fragment>> fragmentStack = new HashMap<>();
This way each "tab" has its own backstack.
I am switching between the fragments with:
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, fragment)
.commitNow();
The fragments are maintained in the Map, so when switching to existing fragment I use it's already initialized view.
Sometimes when I switch from one fragment to second fragment, the second fragment displays menu items from a previously created fragment in the stack despite the fact that onCreateOptionsMenu is called correctly on the second fragment. How do I overcome this unwanted behavior?
The actions that lead to this behavior:
1. Starting at first tab. Map initialized at key 0, with List<fragment> with one fragment, called A. This fragment then displayed. (fragment A has menu items) 2. Navigating to second tab. Map initialized at key 1, with List<fragment> with one fragment, called B, this fragment displayed (replacing fragment A). Fragment B also has menu items. 3. Navigating back to first tab. Previous fragment A is used including it's already initialized view (not inflating a new one). 4. Clicking something with navigates to a new fragment C staying at the same tab. fragment C is added to the fragment list, under the key 0 of the Map. fragment C has no menu items.
5. Navigating to second tab. Previous fragment B is displayed but with menu items of fragment A!
check onCreateOptionsMenu is called for all fragment or not.
I found the problem eventually.
The fragment's onCreateView looked this way:
if (rootView == null) {
// Init view logic and inflation, and:
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
}
return rootView;
moving the ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); outside the if statement fixed the issue.
Related
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
I'm making a view whose architecture is:
A fragment has view pager that has FragmentStatePagerAdapter(adapter 1), FragmentStatePagerAdapter (adapter 1) returns a Fragment when click on Fragment it opens another Fragment that again has a ViewPager and FragmentStatePagerAdapter(adapter 2) attached to it that again returns a Fragment.
My problem is when I'm clicking on Fragment and returning by pressing back button the Fragment is blank, also when I'm using getChildFragmentManager on adapter 1, application crashing that no view found for Fragment.
Edit:
by debugging I found that FragmentStatePagerAdapter wont return fragment that was returned previously thats why I'm getting blank page
In a fragment:
viewPagerDetail.setAdapter(new ProductDetailViewPagerAdapter(getFragmentManager()));
tabLayoutDetail.setupWithViewPager(viewPagerDetail);
(if I'm using getChiildFragmentManager here it crashing application)
Inside ProductDetailViewPagerAdapter:
#Override
public Fragment getItem(int position) {
return new ViewPagerImageFragment();
}
Inside ViewPagerImageFragment: there is single imageview when I click on image view it opens a fragment that has viewpager that has adapter attach on it that also returns a fragment when I press back to fragment that has ProductDetailViewPagerAdapter the viewpager is blank
For the fragment like these kind of nested, we should see for hierarchy of the fragment either we want to see parallel or step ahead or below for step ahead or below choose getFragment manager when replacing and
getParentFragment().getFragmentManager()
for parallel fragment replacement
Happy coding!
I have 3 Fragments and the flow is like
Fragment 1 --> Fragment 2 (adding it to back stack)--> Fragment 3 --> Fragment 1
As I want to go to Fragment 1 from Fragment 3 again, I want the back stack to be clean so that when user press back nothing happens.
Intially I have cleared popback stack at Fragment 3
Boolean isPoped = fManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
And then created a new Instance of my fragment 1
HomeFragment homeFragment = HomeFragment.newInstance(mobileNumber, "");
FragmentTransaction transaction =fManager.beginTransaction();
transaction.replace(R.id.fragment_container, homeFragment);
transaction.commit();
which caused
android: ViewPager throwing null pointer
then I commented the above code and only used
Boolean isPoped = fManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
which works fine as it removed the transactions(that I belive) and took me to homeFragment but that caused View pager not instantiating post fragment popBackStackImmediate
Here I am not getting how popBackStack works? All I want is to take user from Fragment 3 to Fragment 1 and clear the backstack.
I looked at your code in the other question and I think I understand what the problem is.
So your first action is that you create Fragment 1 and put it in a container. I don't see code for that but no matter; if the container is empty then add and replace act the same way. So now your Fragment 1 is in the container.
You haven't called addToBackStack here so the FragmentManager doesn't know how to undo this operation. That's fine, it's the first operation and doesn't need to be undone.
Next you create Fragment 2 and do a replace transaction. Fragment 1 is replaced with Fragment 2. This is pushed to the back stack so the FragmentManager does know how to undo this transaction.
Next you create Fragment 3 and do a replace transaction. Fragment 2 is replaced with Fragment 3. You don't push this operation to the back stack. Uh oh.
Now when you say popBackStack (deferred or immediate) the FragmentManager goes to undo the operation to replace Fragment 2 with Fragment 1 but Fragment 2 isn't in the container anymore. So (from what I understand) the FragmentManager will put Fragment 1 back in the container but leave Fragment 3 there. It doesn't know what to do with Fragment 3.
Here is how I handle those kinds of scenarios: Say I have Fragment 1, then navigate to Fragment 2. To navigate to Fragment 3 when I intend the back button to go to Fragment 1, I do this:
Pop the back stack (immediate, now Fragment 1 is back in the container) then
Replace Fragment 1 with Fragment 3 and push that operation to the back stack.
Now when I press the back button, the FragmentManager knows how to undo my operation and puts Fragment 1 back where Fragment 3 was. This means that I don't have to perform any fragment transactions to handle this back navigation, the FragmentManager handles it all for me.
I need to create navigation of fragments like in Gmail app. It's like: we have one main fragment A, we can open another fragment (B,C,D...) from navigation drawer, and when we open new fragment, it`s open on top of main fragment, and when press back button, in all cases we come back to main fragment A, don't depend of count new opened fragments. It's seems, first main fragment A we use add method(int FragmentTransaction) without adding to fragment backStack. Then, next fragment B we use method add too, with adding to back stack. And when I need to open another one (Fragment C), I need to replace second fragment B. But, when I use method replace(), replaced all container, and main fragment A not showing when back button pressed from fragment C or B and app close. So, the question is: how to replace only fragment B or C, without losing fragment A?
A valid solution would be to have two container framelayouts in your activity. The first one (which will below the other one) contains your fragment A. Everything you open will be added/replaced in the second container.
Another solution is to include the fragment A statically in your layout and have your container framelayout on top of it where you add your fragments B, C, D etc.
open fragment Like this
HighlightFragment highlightFragment=new HighlightFragment(FirstReaderScreen.this); //Your fragment
getSupportFragmentManager()
.beginTransaction()
.add(R.id.LL_Fragment, highlightFragment) // LL_fragment is container
.addToBackStack(null)
.commit();
and in Activity OnBackPress
#Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
In my app, I have the following architecture:
MainActivity
|_ FirstFragment
| |_ GridFragment
| |_ MapFragment
|
|_ SecondFragment
|
|_ ThirdFragment
Please note that:
MainActivity is using ActionBarSherlock and SlidingMenu.
SlidingMenu switches between fragments (FirstFragment, SecondFragment, ThirdFragment).
FirstFragment creates a button into the action bar to switch between grid and map mode. This is, to switch between its subfragments.
What I want to achieve
I need fragments need to preserve its state when switching between them. This is, if I am in FirstFragment and then select map mode (show MapFragment subfragment), and from the slide menu choose another option and come back, I should see the map again. Right now, it's resetting each fragment when selecting its section from the slide menu.
This isn't surprising, as at the moment I am commiting transactions with new FirstFragment() when the menu options are pressed.
However, I first tried to have references to each fragment into his parent. For example, MainActivity had three fragments members which, when commiting transactions, were checked if exist previously and instantiated if necessary. This worked fine until I added the second level of fragments, then it started throwing exceptions when committing the transaction (saying that the Activity was destroyed).
As you may have noticed, I am far from being an Android expert and need some guidance on this topic.
How can I preserve fragment states without having a reference for each of them?
Next thing I tried after posting the question, was to avoid keeping the fragments in local variables inside its container and using FragmentManager.getFragmentByTag() to access them at any given time, but the problem persisted, as FragmentTransaction.replace() was destroying the fragments.
My solution
As Luksprog pointed out in his comment, I had to manage all fragments manually. In order to achieve this, I had to go back to my former approach, where I had local variables for each fragment. Then, MainActivity does the following:
Instantiate its three fragments.
this.firstFragment = new FirstFragment();
this.secondFragment = new SecondFragment();
this.thirdFragment = new ThirdFragment();
Attach the three fragments, and hide all of them except the initial section.
getSupportFragmentManager()
.beginTransaction()
.add(R.id.content_frame, this.firstFragment)
.add(R.id.content_frame, this.secondFragment)
.add(R.id.content_frame, this.thirdFragment)
.hide(this.secondFragment)
.hide(this.thirdFragment)
.commit();
To switch content, SlideMenu is calling this function:
public void switchContent(Fragment newContent) {
if (newContent != null) {
getSupportFragmentManager()
.beginTransaction()
.detach(this.firstFragment)
.detach(this.secondFragment)
.detach(this.thirdFragment)
.attach(newContent)
.commit();
// Restore menu open gesture if map is not present
if (!(newContent instanceof firstFragment) && getSlidingMenu().getTouchModeAbove() != SlidingMenu.TOUCHMODE_FULLSCREEN)
getSlidingMenu().setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
// Set menu open gesture if map is present
if (newContent instanceof firstFragment && firstFragment.currentFragment == FirstFragment.MAP_FRAGMENT)
getSlidingMenu().setTouchModeAbove(SlidingMenu.TOUCHMODE_NONE);
getSlidingMenu().showContent();
}
}
Then, the fragments which has subfragments (FirstFragment) is doing the same but:
It's using getChildFragmentManager() instead of getSupportFragmentManager().
It's using show() and hide() to replace content, because when using detach() and attach() the map wasn't preserving it's coordinates.