My app contains two tabs, inside two tabs every tab has 4-5 nested fragments, inside nested fragments addtobackstack is not working?
How can I add nested fragments to backstack because when I click back button inside nested fragments my app is closing means it's calling super.onBackKeyPressed method.
Add this code in your activity
public void onBackPressed() {
FragmentManager fm = getSupportFragmentManager();
if (fm.getFragments() != null) {
for (Fragment frag : fm.getFragments()) {
if (frag.isVisible()) {
FragmentManager chilFrag = frag.getChildFragmentManager();
if (chilFrag.getBackStackEntryCount() > 0) {
chilFrag.popBackStack();
return;
}
}
}
}
super.onBackPressed();
}
You should use ChildFragmentManager
This is fragment replace function.
fun replace(fragment: Fragment){
childFragmentManager
.beginTransaction()
.replace(R.id.fragmentContainer, fragment)
.commit()
}
And override onBackPressed method in most parent fragment.
override fun onBackPressed() {
val pop = childFragmentManager.popBackStackImmediate()
if (!pop){
super.onBackPressed()
}
}
I wrote with kotlin i hope you can understand.If you need more info, please ask to me.
Use ChildFragmentManager and check if active tab has BackStackEntry, if so then pop backstack else call super.onBackKeyPressed
complete description on this answer:
https://stackoverflow.com/a/37961649/4832356
In my MainActivity, I am launching a fragment using the following:
private void displayView() {
Log.d("displayView", "in select item");
// update the main content by replacing fragments
Fragment fragment = null;
fragment = new WorkoutsFragment();
if (fragment != null) {
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.main_container, fragment)
.addToBackStack("fragBack")
.commit();
} else {
// error in creating fragment
Log.e("MainActivity", "Error in creating fragment");
}
}
This loads my fragment correctly, and I am able to see it, however, when I hit the back button it exits the application. I would like it to go back to MainActivity if possible.
Is this improper handling of a fragment? If so, what would be the correct way of approaching this?
Thanks!
You can override onBackPressed in your main activity and not call super.onBackPressed. In the overriden method, you can remove the fragment from the fragment manager.
I know how to add a Fragment to the backstack but how do I know, when the user presses the back Button, which fragment I left and which I went to? I need to do a certain action depending on this so I need to know from and to which fragment I am going. Specifically I need to know which fragment I left so if it is a certain fragment, I can remove a button.
Override onBackPressed in Activity:
#Override
public void onBackPressed(){
FragmentManager fm = getSupportFragmentManager();
Fragment f = fm.findFragmentById(R.id.content_frame); // get the fragment that is currently loaded in placeholder
Object tag = f.getTag();
// do handling with help of tag here
// call super method
super.onBackPressed();
}
You can add the Fragment like this :
ArticleMain articalemain = new ArticleMain();
getFragmentManager().beginTransaction().add(R.id.fragment_Top, articalemain, "MY_FRAGMENT").commit();
While Removing the Fragments do like this :
ArticleMain myFragment = (ArticleMain) getFragmentManager()
.findFragmentByTag("MY_FRAGMENT");
if (myFragment.isVisible()) {
// add your code here
myFragment.getFragmentManager().beginTransaction()
.remove(myFragment).commit();
}
I have an Activity and many fragments inflated in same FrameLayout
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
example: mainActivity > any fragment (press back button) > activity is blank.
In onCreate:
layout = (FrameLayout)findViewById(R.id.content_frame);
layout.setVisibility(View.GONE);
When I start a fragment:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, profileFragment);
ft.addToBackStack(null);
ft.commit();
layout.setVisibility(View.VISIBLE);
I suppose I need to make the frameLayout's visibility GONE again on back pressed, but how do I do this?
I tried onBackPressed and set layout.setVisibility(View.GONE); but I cannot go back through fragments, as I go directly to main page.
If you have more than one fragment been used in the activity or even if you have only one fragment then the first fragment should not have addToBackStack defined. Since this allows back navigation and prior to this fragment the empty activity layout will be displayed.
// fragmentTransaction.addToBackStack() // dont include this for your first fragment.
But for the other fragment you need to have this defined otherwise the back will not navigate to earlier screen (fragment) instead the application might shutdown.
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
}
else {
int fragments = getSupportFragmentManager().getBackStackEntryCount();
if (fragments == 1) {
finish();
} else if (getFragmentManager().getBackStackEntryCount() > 1) {
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
}
To add a fragment
getSupportFragmentManager().beginTransaction()
.replace(R.id.layout_main, dashboardFragment, getString(R.string.title_dashboard))
.addToBackStack(getString(R.string.title_dashboard))
.commit();
Sorry for the late response.
You don't have to add ft.addToBackStack(null); while adding first fragment.
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, profileFragment);
// ft.addToBackStack(null); --remove this line.
ft.commit();
// ... rest of code
If you want to track by the fragments you should override the onBackPressed method, like this
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() == 1) {
finish();
} else {
super.onBackPressed();
}
}
You can override onBackPressed and check to see if there is anything on the backstack.
#Override
public void onBackPressed() {
int fragments = getFragmentManager().getBackStackEntryCount();
if (fragments == 1) {
// make layout invisible since last fragment will be removed
}
super.onBackPressed();
}
Just don't add the first fragment to back stack
Here is the Kotlin code that worked for me.
val ft = supportFragmentManager.beginTransaction().replace(container, frag)
if (!supportFragmentManager.fragments.isEmpty()) ft.addToBackStack(null)
ft.commit()
On a recent personal project, I solved this by not calling addToBackStack if the stack is empty.
// don't add the first fragment to the backstack
// otherwise, pressing back on that fragment will result in a blank screen
if (fragmentManager.getFragments() != null) {
transaction.addToBackStack(tag);
}
Here's my full implementation:
String tag = String.valueOf(mCurrentSectionId);
FragmentManager fragmentManager = mActivity.getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag(tag);
if (fragment != null) {
// if the fragment exists then no need to create it, just pop back to it so
// that repeatedly toggling between fragments doesn't create a giant stack
fragmentManager.popBackStackImmediate(tag, 0);
} else {
// at this point, popping back to that fragment didn't happen
// So create a new one and then show it
fragment = createFragmentForSection(mCurrentSectionId);
FragmentTransaction transaction = fragmentManager.beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.replace(R.id.main_content, fragment, tag);
// don't add the first fragment to the backstack
// otherwise, pressing back on that fragment will result in a blank screen
if (fragmentManager.getFragments() != null) {
transaction.addToBackStack(tag);
}
transaction.commit();
}
irscomp's solution works if you want to end activity when back button is pressed on first fragment. But if you want to track all fragments, and go back from one to another in back order, you add all fragments to stack with:
ft.addToBackStack(null);
and then, add this to the end of onCreate() to avoid blank screen in last back pressed; you can use getSupportFragmentManager() or getFragmentManager() depending on your API:
FragmentManager fm = getSupportFragmentManager();
fm.addOnBackStackChangedListener(new OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if(getSupportFragmentManager().getBackStackEntryCount() == 0) finish();
}
});
Final words: I don't suggest you to use this solution, because if you go from fragment1 to fragment 2 and vice versa 10 times, when you press back button 10 times it will do it in back order which users will not want it.
Almost same as Goodlife's answer, but in Xamarin.Android way:
Load fragment (I wrote helper method for that, but it's not necessary):
public void LoadFragment(Activity activity, Fragment fragment, string fragmentTitle = "")
{
var fragmentManager = activity.FragmentManager;
var fragmentTransaction = fragmentManager.BeginTransaction();
fragmentTransaction.Replace(Resource.Id.mainContainer, fragment);
fragmentTransaction.AddToBackStack(fragmentTitle);
fragmentTransaction.Commit();
}
Back button (in MainActivity):
public override void OnBackPressed()
{
if (isNavDrawerOpen()) drawerLayout.CloseDrawers();
else
{
var backStackEntryCount = FragmentManager.BackStackEntryCount;
if (backStackEntryCount == 1) Finish();
else if (backStackEntryCount > 1) FragmentManager.PopBackStack();
else base.OnBackPressed();
}
}
And isNavDrawerOpen method:
bool isNavDrawerOpen()
{
return drawerLayout != null && drawerLayout.IsDrawerOpen(Android.Support.V4.View.GravityCompat.Start);
}
I still could not fix the issue through getBackStackEntryCount() and I solved my issue by making the main page a fragment too, so in the end I have an activity with a FrameLayout only; and all other fragments including the main page I inflate into that layout. This solved my issue.
I had the same problem when dealing with Firebase's Ui Login screen. When back button was pressed it left a blank screen.
To solve the problem I just called finish() in my onStop() method for said Activity. Worked like a charm.
If you have scenario like me where a list fragment opens another details fragment, and on back press you first need to show the list fragment and then get out the whole activity then, addToBackStack for all the fragment transactions.
and then on the activity, do like this (courtesy: #JRomero's answer, #MSaudi's comment)
#Override
public void onBackPressed() {
int fragments = getFragmentManager().getBackStackEntryCount();
if (fragments == 1) {
// make layout invisible since last fragment will be removed
}
super.onBackPressed();
}
Just Comment or Remove transaction.addToBackStack(null) in your code.Below is code to change fragment in kotlin.
fun ChangeFragment(activity: MainActivity, fragment: Fragment) {
val transaction: FragmentTransaction =
activity.getSupportFragmentManager().beginTransaction()
transaction.replace(R.id.tabLayoutContainer, fragment)
transaction.commit()
}
I am playing with fragments in Android.
I know I can change a fragment by using the following code:
FragmentManager fragMgr = getSupportFragmentManager();
FragmentTransaction fragTrans = fragMgr.beginTransaction();
MyFragment myFragment = new MyFragment(); //my custom fragment
fragTrans.replace(android.R.id.content, myFragment);
fragTrans.addToBackStack(null);
fragTrans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
fragTrans.commit();
My question is, in a Java file, how can I get the currently displayed Fragment instance?
When you add the fragment in your transaction you should use a tag.
fragTrans.replace(android.R.id.content, myFragment, "MY_FRAGMENT");
...and later if you want to check if the fragment is visible:
MyFragment myFragment = (MyFragment)getSupportFragmentManager().findFragmentByTag("MY_FRAGMENT");
if (myFragment != null && myFragment.isVisible()) {
// add your code here
}
See also http://developer.android.com/reference/android/app/Fragment.html
I know it's an old post, but was having trouble with it previously too. Found a solution which was to do this in the onBackStackChanged() listening function
#Override
public void onBackPressed() {
super.onBackPressed();
Fragment f = getActivity().getFragmentManager().findFragmentById(R.id.fragment_container);
if(f instanceof CustomFragmentClass)
// do something with f
((CustomFragmentClass) f).doSomething();
}
This worked for me as I didn't want to iterate through every fragment I have to find one that is visible.
Here is my solution which I find handy for low fragment scenarios
public Fragment getVisibleFragment(){
FragmentManager fragmentManager = MainActivity.this.getSupportFragmentManager();
List<Fragment> fragments = fragmentManager.getFragments();
if(fragments != null){
for(Fragment fragment : fragments){
if(fragment != null && fragment.isVisible())
return fragment;
}
}
return null;
}
Every time when you show fragment you must put it tag into backstack:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setTransition(FragmentTransaction.TRANSIT_ENTER_MASK);
ft.add(R.id.primaryLayout, fragment, tag);
ft.addToBackStack(tag);
ft.commit();
And then when you need to get current fragment you may use this method:
public BaseFragment getActiveFragment() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
return null;
}
String tag = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1).getName();
return (BaseFragment) getSupportFragmentManager().findFragmentByTag(tag);
}
Kotlin way;
val currentFragment = supportFragmentManager.fragments.last()
What I am using to find current displaying fragment is in below code. It is simple and it works for me by now. It runs in the activity which holds the fragments
FragmentManager fragManager = this.getSupportFragmentManager();
int count = this.getSupportFragmentManager().getBackStackEntryCount();
Fragment frag = fragManager.getFragments().get(count>0?count-1:count);
The reactive way:
Observable.from(getSupportFragmentManager().getFragments())
.filter(fragment -> fragment.isVisible())
.subscribe(fragment1 -> {
// Do something with it
}, throwable1 -> {
//
});
My method is based on try / catch like this :
MyFragment viewer = null;
if(getFragmentManager().findFragmentByTag(MY_TAG_FRAGMENT) instanceOf MyFragment){
viewer = (MyFragment) getFragmentManager().findFragmentByTag(MY_TAG_FRAGMENT);
}
But there may be a better way ...
If you are using the AndroidX Navigation:
val currentFragment = findNavController(R.id.your_navhost)?.currentDestination
For more info on this navigation component:
https://developer.android.com/guide/navigation/navigation-getting-started
Well, this question got lots of views and attention but still did not contained
the easiest solution from my end - to use getFragments().
List fragments = getSupportFragmentManager().getFragments();
mCurrentFragment = fragments.get(fragments.size() - 1);
You can query which fragment is loaded into your Activities content frame, and retrieve the fragment class, or fragment 'simple name' (as a string).
public String getCurrentFragment(){
return activity.getSupportFragmentManager().findFragmentById(R.id.content_frame).getClass().getSimpleName();
}
Usage:
Log.d(TAG, getCurrentFragment());
Outputs:
D/MainActivity: FragOne
If get here and you are using Kotlin:
var fragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
R.id.fragment_container is the id where the fragment is presenting on their activity
Or if you want a nicer solution:
supportFragmentManager.findFragmentById(R.id.content_main)?.let {
// the fragment exists
if (it is FooFragment) {
// The presented fragment is FooFragment type
}
}
It's a bit late, But for anyone who is interested :
If you know the index of the your desired fragment in FragmentManager just get a reference to it and check for isMenuVisible() function! here :
getSupportFragmentManager().getFragments().get(0).isMenuVisible()
If true Its visible to user and so on!
1)
ft.replace(R.id.content_frame, fragment, **tag**).commit();
2)
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment currentFragment = fragmentManager.findFragmentById(R.id.content_frame);
3)
if (currentFragment.getTag().equals(**"Fragment_Main"**))
{
//Do something
}
else
if (currentFragment.getTag().equals(**"Fragment_DM"**))
{
//Do something
}
There's a method called findFragmentById() in SupportFragmentManager. I use it in the activity container like :
public Fragment currentFragment(){
return getSupportFragmentManager().findFragmentById(R.id.activity_newsfeed_frame);
}
That's how to get your current Fragment. If you have custom Fragment and need to check what Fragment it is, I normally use instanceof :
if (currentFragment() instanceof MyFrag){
// Do something here
}
This should work -
val visibleFragment = supportFragmentManager.fragments.findLast { fgm -> fgm.isVisible }
Timber.d("backStackIterator: visibleFragment: $visibleFragment")
Inspired by Tainy's answer, here is my two cents. Little modified from most other implementations.
private Fragment getCurrentFragment() {
FragmentManager fragmentManager = myActivity.getSupportFragmentManager();
int stackCount = fragmentManager.getBackStackEntryCount();
if( fragmentManager.getFragments() != null ) return fragmentManager.getFragments().get( stackCount > 0 ? stackCount-1 : stackCount );
else return null;
}
Replace "myActivity" with "this" if it is your current activity or use reference to your activity.
This is simple way to get current fragment..
getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override public void onBackStackChanged() {
currentFragment = fragmentManager.findFragmentById(R.id.content);
if (currentFragment != null && (currentFragment instanceof LoginScreenFragment)) {
logout.setVisibility(View.GONE);
} else {
logout.setVisibility(View.VISIBLE);
}
}
});
Checkout this solution. It worked for me to get the current Fragment.
if(getSupportFragmentManager().getBackStackEntryCount() > 0){
android.support.v4.app.Fragment f =
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if(f instanceof ProfileFragment){
Log.d(TAG, "Profile Fragment");
}else if(f instanceof SavedLocationsFragment){
Log.d(TAG, "SavedLocations Fragment");
}else if(f instanceof AddLocationFragment){
Log.d(TAG, "Add Locations Fragment");
}
it's so simple, not that much code you need to write
yourFragment.isAdded()
or
yourFragment.isVisible();
I prefer isAdded(),both of them return boolean value use it in if condition and must initialize your fragment in onCreate() otherwise you will get null point exception.
None of the above 30 answers fully worked for me. But here is the answer that worked:
Using Kotlin, when using Navigation Component:
fun currentVisibleFragment(): Fragment? {
return supportFragmentManager.fragments.first()?.getChildFragmentManager()?.getFragments()?.get(0)
}
Sev's answer works for when you hit the back button or otherwise change the backstack.
I did something slightly different, though. I have a backstack change listener setup on a base Fragment and its derived fragments and this code is in the listener:
Fragment f = getActivity().getSupportFragmentManager().findFragmentById(R.id.container);
if (f.getClass().equals(getClass())) {
// On back button, or popBackStack(),
// the fragment that's becoming visible executes here,
// but not the one being popped, or others on the back stack
// So, for my case, I can change action bar bg color per fragment
}
Easy way to do that :
Fragment fr=getSupportFragmentManager().findFragmentById(R.id.fragment_container);
String fragmentName = fr.getClass().getSimpleName();
I had to do this very recently
public Fragment getCurrentFragment() {
return fragmentManager.findFragmentById(R.id.container);
}
and finaly i got last fragment on this container.
final FragmentManager fm=this.getSupportFragmentManager();
final Fragment fragment=fm.findFragmentByTag("MY_FRAGMENT");
if(fragment != null && fragment.isVisible()){
Log.i("TAG","my fragment is visible");
}
else{
Log.i("TAG","my fragment is not visible");
}
If you are getting the current instance of Fragment from the parent activity you can just
findFragmentByID(R.id.container);
This actually get's the current instance of fragment that's populated on the view. I had the same issue. I had to load the same fragment twice keeping one on backstack.
The following method doesn't work. It just gets a Fragment that has the tag. Don't waste your time on this method. I am sure it has it's uses but to get the most recent version of the same Fragment is not one of them.
findFragmentByTag()
Kotlin safer way than exposed here
supportFragmentManager.fragments.lastOrNull()?.let { currentFragment ->
//Do something here
}
This is work for me. I hope this will hepl someone.
FragmentManager fragmentManager = this.getSupportFragmentManager();
String tag = fragmentManager
.getBackStackEntryAt(
fragmentManager
.getBackStackEntryCount() - 1)
.getName();
Log.d("This is your Top Fragment name: ", ""+tag);
I found findFragmentByTag isn't that convenient. If you have String currentFragmentTag in your Activity or parent Fragment, you need to save it in onSaveInstanceState and restore it in onCreate. Even if you do so, when the Activity recreated, onAttachFragment will called before onCreate, so you can't use currentFragmentTag in onAttachFragment(eg. update some views based on currentFragmentTag), because it's might not yet restored.
I use the following code:
Fragment getCurrentFragment() {
List<Fragment> fragments = getSupportFragmentManager().getFragments();
if(fragments.isEmpty()) {
return null;
}
return fragments.get(fragments.size()-1);
}
The document of FragmentManager state that
The order of the fragments in the list is the order in which they were added or attached.
When you need to do stuff based on current fragment type, just use getCurrentFragment() instance of MyFragment instead of currentFragmentTag.equals("my_fragment_tag").
Note that getCurrentFragment() in onAttachFragment will not get the attaching Fragment, but the previous attached one.
getSupportFragmentManager().findFragmentById(R.id.content_frame).getClass().getSimpleName();
Well, I guess this is the most straight forward answer to this question.
I hope this helps.