Why does my fragment manager's getBackStackEntryCount method return zero?
I've spent an hour or checking SO for an answer to this question. I've
used the fragment support manager with getSupportFragmentManager
called executePendingTransactions
imported android.support.v4.app.Fragment
but getBackStackEntryCount keeps returning zero.
Any idea why? Doesn't my code look correct?
Here's my code
private void injectFragment(){
// Get the fragment
StepsFragment stepsFragment = new StepsFragment();
// Get the support fragment manager
FragmentManager fragmentManager = getSupportFragmentManager();
// Inject the fragment into a frame layout
fragmentManager.beginTransaction()
.replace(R.id.frame_layout, stepsFragment)
.commit();
// I read that I should execute pending transactions before calling
// getBackStackEntryCount
fragmentManager.executePendingTransactions();
// This log prints `getBackStackEntryCount returns 0`
int entryCount = fragmentManager.getBackStackEntryCount();
Log.v(TAG,"getBackStackEntryCount returns "+String.valueOf(entryCount));
}
getBackStackEntryCount() is 0 because you have not added any Fragment transactions to the FragmentManager's back stack.
Note that each FragmentManager has its own backstack (so a Fragment's child FragmentManager stack would be different from the containing Activity's fragment backstack), and it is also distinct from the application's Activity back stack.
If you were to add your transaction to the back stack like so, it would return 1:
fragmentManager.beginTransaction()
.replace(R.id.frame_layout, stepsFragment)
.addToBackStack(null)
.commit();
Related
I have 3 fragments: Home, A and B. Home and A are in mobile navigation menu.
User goes from A to B and then press back button. The issue that if I use getFragmentManager().popBackStack(); onCreateView of fragment A is calling and I have duplicating of content.
But if I use getActivity().onBackPressed(); it goes to Home fragment instead of A.
How to display fragment A by clicking back button without refreshing the view?
Here is how I make transaction from A to B
CertificateItemFragment certificateItemFragment = new CertificateItemFragment(item);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.nav_host_fragment, certificateItemFragment).addToBackStack(null);
fragmentTransaction.commit();
You need to know some premise before using FragmentManager.
in Activity there is FragmentManager and we should call it with getSupportFragmentManager(), getFragmentManager() is deprecated.
in Fragment there are multiple FragmentManagers called ParentFragmentManager and ChildFragmentManager, the final FragmentManager which is deprecated too. And ParentFragmentManager will be same as Activity's FragmentManager
getActivity().onBackPressed() will pull out fragment if any stack exists in Activity's FragmentManager
fragmentManager.popBackStack() will pull out fragment if any stack exists in Activity's or Fragment's FragmentManager rely on who calls
Base on above points
(1) If You want to hold fragments in Activity, you should call getSupportFragmentManager() in Activity, and call getParentManager() in Fragment, then onBackPressed() will pull out the fragment you last add to stack.
(2) If you want to hold fragments in a Fragment and Separated from Activity, you should call getChildFragmentManager() in Fragment, then activity.onBackPressed() will pull out the fragment which in Activity's fragment stack and ignore other fragments in Fragment's fragment stack.
For question will be same as case (1), and if you dont want re-create fragment, you should use add() instead of replace()
in Fragment A
CertificateItemFragment certificateItemFragment = new CertificateItemFragment(item);
FragmentManager fragmentManager = getParentFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.nav_host_fragment, certificateItemFragment).addToBackStack(CertificateItemFragment.TAG);
fragmentTransaction.commit();
now onBackPressed() will backstack from CertificateItemFragment to Pre-Fragment
In normal case navigation item list menu open fragment which are replace to each other but when we move inside of any item as like in your case move from fragment A to B,
In this case normally we use another activity on which create fragment.
If you do't want to use activity then, you just need to add fragment.
try below code that may help you
CertificateItemFragment certificateItemFragment = new CertificateItemFragment(item);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.nav_host_fragment, certificateItemFragment).addToBackStack(CertificateItemFragment.TAG);
fragmentTransaction.commit();
So, I have this code inside a setOnClickListener:
helpFragment = HelpFragment.newInstance()
supportFragmentManager
.beginTransaction() // Começar a transição
.replace(R.id.container, helpFragment)
.addToBackStack(helpFragment.toString())
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit() // Aplicar as alterações
}
But the problem is, every time I click the button a new instance of the fragment is instantiated. With that, for example, if I click 10 times in the button, I will have 9 fragments added to backstack and 1 visible. How can I create just one instance of the fragment? I have tried:
if (helpFragment == null)
But that obviously doesn't work...
Adding a Fragment to the back stack will retain fragments in the stack so that you can navigate back when needed.
You can still use the back stack but you have to check if the fragment is already added so that you don't have duplicated instances of the fragment in the stack.
E.g.
val helpFragment = HelpFragment.newInstance()
val isInBackstack = supportFragmentManager.findFragmentByTag(helpFragment.toString())
if (!isInBackstack) {
supportFragmentManager
.beginTransaction() // Começar a transição
.replace(R.id.container, helpFragment)
.addToBackStack(helpFragment.toString())
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit() // Aplicar as alterações
}
If you use addToBackStack, it'll always save the fragments to the backstack. Remove that line to not add the fragment to backstack. addToBackStack is used when there are multiple changes to the transaction, then all the changes are added in stack, and pressing back button will then restore those transactions one by one.
helpFragment = HelpFragment.newInstance()
supportFragmentManager
.beginTransaction() // Começar a transição
.replace(R.id.container, helpFragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit() // Aplicar as alterações
}
It is better to see whole codebase of your problem. You can solve it with Kotlin's lazy. Check out this topic
I want to call a fragment method from its parent activity. For that i want object of fragment.
parent activity have fragment in framelayout like this:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/bottom_buttons"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
This is the code for getting fragment object.
FragmentBottomButtons fragment = new FragmentBottomButtons();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.bottom_buttons, fragment,"FragmentTag");
//ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
//ft.addToBackStack("");
ft.commit();
/*
getSupportFragmentManager()
.beginTransaction()
.add(R.id.bottom_buttons, new FragmentBottomButtons())
.commit();
*/
frag = (FragmentBottomButtons) getSupportFragmentManager().findFragmentByTag("FragmentTag");
//fragmentBottomButtons = (FrameLayout)findViewById(R.id.bottom_buttons);
if (frag == null){
Utility.displayToast("fragmnt is null");
}
But it is returning null.
can anyone help me on this? what is wrong here?
When you using commit() method nobody gives to you guarantee that your fragment will be attached to FragmentManager on the fly.
This happens due to inner FragmentManager logic : when you add\replace\remove any fragments you create a FragmentTransaction and puts it into execution queue inside FragmentManager. Actually all Transactions waiting until FragmentManager enqueue tasks.
To avoid this situation you can force enqueue each task under Fragment via
getSupportFragmentManager().executePendingTranscation();
This method starts pending Transactions and make more hard sure for use findFragmentById() method.
So, finally you need :
CustomFragment fragment = new CustomFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
//add or replace or remove fragment
ft.commit();
getSupportFragmentManager().executePendingTranscation();
CustomFragment customFragment = (CustomFragment) getSupportFragmentManager().findFragmentByTag("FragmentTag");
Your Fragment is not attached yet. You need to call findFragmentByTag() later on. You can for exemple add a listener to you Fragment and call your Activity on the onAttach() method to tell your Activity that your Fragment is created.
getFragmentManager().beginTransaction()
.replace(R.id.graph_fragment_holder, new GraphFragment(), "GRAPH_FRAGMENT")
.commit();
getFragmentManager().beginTransaction()
.replace(R.id.list_fragment_holder, new ListFragment(), "LIST_FRAGMENT")
.commit();
//getFragmentManager().executePendingTransactions();
GraphFragment graphFragment = (GraphFragment) getFragmentManager().findFragmentByTag("GRAPH_FRAGMENT");
graphFragment.setData(data);
ListFragment listFragment = (ListFragment) getFragmentManager().findFragmentByTag("LIST_FRAGMENT");
listFragment.setData(data);
I've supplied a tag so I'm not sure why findFragmentByTag() returns null.
What I've tried from reading other questions:
this.setRetainInstance(true) in the oncreate of both fragments.
Both fragment constructors are empty public fragmentName(){}.
tried executePendingTransactions after adding the fragments.
tried add instead of replace on the fragments (edited)
I was confused about this for a long time. First, you need to save the fragment you are replacing by pushing it onto the back stack. The tag you supply is put on the fragment you are adding, not the one you are pushing onto the back stack. Later, when you do push it onto the back stack, that tag goes with it. Here's code with objects broken out to make it easier to trace. You must call 'addToBackStack' before 'commit'.
GraphFragment grFrag = new GraphFragment();
FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
tr.replace(R.id.fragment_container, grFrag, "GRAPH_FRAGMENT");
// grFrag is about to become the current fragment, with the tag "GRAPH_FRAGMENT"
tr.addToBackStack(null);
// 'addToBackStack' also takes a string, which can be null, but this is not the tag
tr.commit();
// any previous fragment has now been pushed to the back stack, with it's tag
ListFragment liFrag = new ListFragment();
FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
tr.replace(R.id.fragment_container, liFrag, "LIST_FRAGMENT");
// liFrag is is about to become the current fragment, with the tag "LIST_FRAGMENT"
tr.addToBackStack(null);
tr.commit();
// 'grFrag' has now been pushed to the back stack, with it's tag being "GRAPH_FRAGMENT"
Call getFragmentManager().executePendingTransactions() after fragment transaction.
getFragmentManager()
.beginTransaction()
.replace(R.id.container, new ExampleFragment(), "YOUR TAG HERE");
.commit();
//after transaction you must call the executePendingTransaction
getFragmentManager().executePendingTransactions();
//now you can get fragment which is added with tag
ExampleFragment exampleFragment = getFragmentManager().findFragmentByTag("YOUR TAG HERE");
I was having the same problem of findFragmentByTag() always returning null.
Eventually I tracked it down, I was overriding onSaveInstanceState() in my Activity but not calling super. As soon as I fixed that findFragmentByTag() returned the Fragment as expected.
You can use
fragmentTransaction.addToBackStack(yourFragmentTag);
After that you can reuse it with
getSupportFragmentManager().findFragmentByTag(yourFragmentTag);
Answered here, just need to call getSupportFragmentManager().executePendingTransactions(); after your findByTag or findById
In my case I had to create a class level FragmentManager object and then use it instead of using getSupportFragmentManager() directly.
public class Main extends BaseActivity {
FragmentManager fragmentManager;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragmain);
fragmentManager = getSupportFragmentManager();
initFrag1();
}
private void initFrag1() {
String name = Frag1.class.getSimpleName();
if (fragmentManager.findFragmentByTag(name) == null) {
fragmentManager.beginTransaction()
.add(R.id.frag_container, new Frag1(), name)
.addToBackStack(name)
.commit();
}
}
}
How can I reset or reload a fragment container, to make it empty.
I have a master detail view and I want to reset the detail container to empty on a menu item click.This works in some cases and does not in some.
NullFragment fragment = new NullFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
fragmentTransaction.replace(R.id.item_detail_container,
fragment);
int count = fragmentManager.getBackStackEntryCount();
fragmentManager.popBackStackImmediate(count, 0);
fragmentTransaction.commit();
Usually you simply remove the fragment from it.
For example do something like
getFragmentManager().beginTransaction().remove(getFragmentManager().findFragmentById(R.id.your_container)).commit();
this will remove the fragment from the your_container holding it.
This gets the fragment currently present in your_container
getFragmentManager().findFragmentById(R.id.your_container)
and this remove the fragment
getFragmentManager().beginTransaction().remove(fragment).commit();
EDIT
Also sometimes it is useful to ensure all transactions are performed and finished, this can be done by using
getFragmentManager().executePendingTransactions();