Switch Fragments while Maintaining State - android

Decided to rewrite this question:
I have three fragments call them A B C. Each has a view with some fields for the user to fill out. The user should be able to use a menu to switch between the different fragments. If the user fill outs information in fragment A and then switch to C fills out more information and then switches back to A the information the user typed in A should still be there.
I think I need to be using the FragmentManager somehow but I can't figure out the right combination of adds/ replaces / attaches ... that are required to make it work the way I want.
Can someone please provide a code snippet that would allow me to switch between fragments while maintaining each fragments view state.
Current Working Solution:
mContent is the active fragment and is a private member variable of the activity.
If anyone sees something wrong with this approach or a way to make it more efficient / more robust please let me know!
public void switchContent(String fragmentTag) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
if ( fragmentManager.findFragmentByTag( fragmentTag ) != mContent ) {
if ( !mContent.isDetached() ) {
transaction.detach( mContent );
}
if ( fragmentManager.findFragmentByTag( fragmentTag ) == null ) {
if ( fragmentTag.equals( "details" ) ) {
mContent = ScheduleDetailsFragment.newInstance();
} else if ( fragmentTag.equals( "notes" ) ) {
mContent = ScheduleNotesFragment.newInstance();
} else if ( fragmentTag.equals( "exceptions" ) ) {
// #TODO - Create Exceptions Fragment
}
} else {
mContent = ( SherlockFragment ) fragmentManager.findFragmentByTag( fragmentTag );
}
if ( mContent.isDetached() ) {
transaction.attach( mContent );
} else if ( !mContent.isAdded() ) {
transaction.add( R.id.content_frame, mContent, fragmentTag );
}
transaction.commit();
}
getSlidingMenu().showContent();
}
Thank You,
Nathan

I based my code of one of the examples provided but it returns a new fragment everytime a menu item is clicked so state information is lost.
If your pages hold onto some form of state/session, returning a new Fragment for every navigation event is a very poor user experience, and also not so good on memory usage.
I'd recommend having your sliding menu use the FragmentManager in your main Activity to maintain all of your Fragments that you switch between. It will manage all of their states, and you can retrieve them using findFragmentByTag(String tag). You would just need to ensure that each Fragment saves any state (values/data that changes after initial creation) that you add in onSaveInstanceState(Bundle outState) and restores it in onCreate(Bundle savedInstanceState). I'm not sure how the sliding menu you are using manages that currently, so this may not be possible. Hope this helps at least get you going in the right direction.
Edit: Updated answer for updated question -
Here is how I would switch the Fragments. By using replace, you will completely switch the Fragments, while reusing the already created ones. In order for your Fragments to saved/restore their state, you need to refer to my answer above. If that is unclear, search for some examples of how to save state in Fragments and Activities.
public void switchContent(String fragmentTag) {
// If our current fragment is null, or the new fragment is different, we need to change our current fragment
if (mContent == null || !mContent.getTag().equals(fragmentTag)) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
// Try to find the fragment we are switching to
Fragment fragment = fragmentManager.findFragmentByTag(fragmentTag);
// If the new fragment can't be found in the manager, create a new one
if (fragment == null) {
if (fragmentTag.equals("details")) {
mContent = ScheduleDetailsFragment.newInstance();
}
else if (fragmentTag.equals("notes")) {
mContent = ScheduleNotesFragment.newInstance();
}
else if (fragmentTag.equals("exceptions")) {
// #TODO - Create Exceptions Fragment
}
}
// Otherwise, we found our fragment in the manager, so we will reuse it
else {
mContent = (SherlockFragment) fragment;
}
// Replace our current fragment with the one we are changing to
transaction.replace(R.id.content_frame, mContent, fragmentTag);
transaction.commit();
getSlidingMenu().showContent();
}
else {
// Do nothing since we are already on the fragment being changed to
}
}

Related

What's the best practice fo saving states during navigation across Fragments in an Activity AND refreshing data across all the Fragments?

So my current implementation is as follows. Basically I preload all the fragments, set them as fields of the current Activity, then I add or show if the fragment was already added.
mFeedTopicsFragment = FeedTopicsFragment.getInstance();
mUserDiscussionsFragment = UserDiscussionsFragment.getInstance(SessionPersistor.getSignedInUserId());
mUserConversationsFragment = MyConversationsFragment.getInstance(SessionPersistor.getSignedInUserId());
activityMainNavigation.setOnNavigationItemSelectedListener((MenuItem item) -> {
switch (item.getItemId()) {
case R.id.menu_item_go_to_feed_fragment:
if (getSupportFragmentManager().findFragmentByTag(FeedTopicsFragment.class.getSimpleName()) != null) {
getSupportFragmentManager()
.beginTransaction()
.show(mFeedTopicsFragment)
.hide(mUserDiscussionsFragment)
.hide(mUserConversationsFragment)
.commit();
} else {
getSupportFragmentManager()
.beginTransaction()
.add(R.id.activity_main_container_fragment, mFeedTopicsFragment, FeedTopicsFragment.class.getSimpleName())
.hide(mUserDiscussionsFragment)
.hide(mUserConversationsFragment)
.commit();
}
return true;
case R.id.menu_item_go_to_user_discussions_fragment:
if (getSupportFragmentManager().findFragmentByTag(UserDiscussionsFragment.class.getSimpleName()) != null) {
getSupportFragmentManager()
.beginTransaction()
.show(mUserDiscussionsFragment)
.hide(mFeedTopicsFragment)
.hide(mUserConversationsFragment)
.commit();
} else {
getSupportFragmentManager()
.beginTransaction()
.add(R.id.activity_main_container_fragment, mUserDiscussionsFragment, UserDiscussionsFragment.class.getSimpleName())
.hide(mFeedTopicsFragment)
.hide(mUserConversationsFragment)
.commit();
}
return true;
case R.id.menu_item_go_to_user_conversations_fragment:
if (getSupportFragmentManager().findFragmentByTag(MyConversationsFragment.class.getSimpleName()) != null) {
getSupportFragmentManager()
.beginTransaction()
.show(mUserConversationsFragment)
.hide(mFeedTopicsFragment)
.hide(mUserDiscussionsFragment)
.commit();
} else {
getSupportFragmentManager()
.beginTransaction()
.add(R.id.activity_main_container_fragment, mUserConversationsFragment, MyConversationsFragment.class.getSimpleName())
.hide(mFeedTopicsFragment)
.hide(mUserDiscussionsFragment)
.commit();
}
return true;
However, I feel this is a bad approach since I'm wasting memory by preloading the fragments. However, I don't want to call replace each FragmentTransaction as that will call onCreateView each time and force a fresh API call each time I navigate between the fragments. I don't want this, as long as I remain in this Activity, say I start on FeedTopicsFragment, scroll down to the 10th item in the RecyclerView, navigate from FeedTopicsFragment to UserDiscussionsFragment, scroll down to the third item in the RecyclerView, and then back to FeedTopicsFragment, I will still see the 10th item, and back in UserDiscussionsFragment, I will see the third item.
I'm considering using onSaveInstanceState to save a Bundle of the data and load that Bundle in onActivityCreated. But I'm afraid onSaveInstanceState might not be called, neither will onActivityCreated. So what's the best solution for saving the state without creating an instance of the Fragment or saving it as a field and deleting that state once I navigate away from the Activity?
Each fragment currently has a loadData method that, inside that method, makes a call to the API and sets the RecyclerView, TextView, etc fields with the response data. Once I do a SwipeRefresh inside the Activity, it will call loadData on each of the Fragments.
I'm not sure how to do this refresh part either, basically I have to get an instance of all the Fragments and then call loadData on each of them. But only one fragment is in the activity_main_container_fragment at a time. In this case, I guess it makes sense to save an instance of each of the Fragments so then I can just call loadData on those local instances.
Basically you can check your Arraylist if null or empty before calling api.
if list has data then fill in listview locally,
else call api for getting data.
if you load fragment from stack then it will have its all variables like arraylist too.
First of all you should consider calling this method to replace your Fragment, because i saw you wrote too much code.
/**
* replace or add fragment to the container
*
* #param fragment pass android.support.v4.app.Fragment
* #param bundle pass your extra bundle if any
* #param popBackStack if true it will clear back stack
* #param findInStack if true it will load old fragment if found
*/
public void replaceFragment(Fragment fragment, #Nullable Bundle bundle, boolean popBackStack, boolean findInStack) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
String tag = fragment.getClass().getName();
Fragment parentFragment;
if (findInStack && fm.findFragmentByTag(tag) != null) {
parentFragment = fm.findFragmentByTag(tag);
} else {
parentFragment = fragment;
}
// if user passes the #bundle in not null, then can be added to the fragment
if (bundle != null)
parentFragment.setArguments(bundle);
else parentFragment.setArguments(null);
// this is for the very first fragment not to be added into the back stack.
if (popBackStack) {
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
} else {
ft.addToBackStack(parentFragment.getClass().getName() + "");
}
ft.replace(R.id.contenedor_principal, parentFragment, tag);
ft.commit();
fm.executePendingTransactions();
}
use it like
If your fragment is home or dashboard fragment then
Fragment f = new YourFragment();
replaceFragment(f, null, true, true);
Otherwise
Fragment f = new YourFragment();
replaceFragment(f, null, false, true);

fragments sometimes overlap - Android

I have a navigation drawer and clicking on items shows/hides/creates full screen fragments.
For the most part, this code works great. But sometimes, maybe 1% of the time, I will get crazy full screen fragment overlapping when opening the app while it has already been running.
Is the problem with my code..? Or maybe something else in Android where it does not recognize I have the fragments with the tags already created?
Here is the relevant code for how I show/hide/create fragments:
#SuppressWarnings("StatementWithEmptyBody")
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Get to drawer layout so we can interact with it
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
// Get the fragment manager to remove/add fragments
FragmentManager fragmentManager = getSupportFragmentManager();
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_profile) {
// Hide visible fragment
fragmentManager.beginTransaction().hide(getVisibleFragment()).commit();
// Check if the fragment exists first.
if(fragmentManager.findFragmentByTag("profileFragment") != null) {
// If the fragment exists, show it (no reason to recreate it).
fragmentManager.beginTransaction()
.show(fragmentManager.findFragmentByTag("profileFragment"))
.commit();
} else {
// If the fragment does not exist, add it to fragment manager with a tag to identify it.
// Create new fragment instance with required argument(s).
ProfileFragment fragment = ProfileFragment.newInstance();
fragmentManager.beginTransaction()
.add(R.id.content_frame, fragment, "profileFragment")
.commit();
}
// Set the title
mToolbarTitleTextView.setText(R.string.title_activity_profile);
} else if (id == R.id.nav_feed) {
// Hide visible fragment
fragmentManager.beginTransaction().hide(getVisibleFragment()).commit();
// Check if the fragment exists first.
if(fragmentManager.findFragmentByTag("feedFragment") != null) {
// If the fragment exists, show it (no reason to recreate it).
fragmentManager.beginTransaction()
.show(fragmentManager.findFragmentByTag("feedFragment"))
.commit();
} else {
// If the fragment does not exist, add it to fragment manager with a tag to identify it.
fragmentManager.beginTransaction()
.add(R.id.content_frame, new feedFragment(), "feedFragment")
.commit();
}
// Set the title
mToolbarTitleTextView.setText(R.string.title_activity_feed);
} else if (id == R.id.nav_notifications) {
// Hide visible fragment
fragmentManager.beginTransaction().hide(getVisibleFragment()).commit();
// Hide the post button
mPostButton.setVisibility(View.GONE);
// Check if the fragment exists first.
if(fragmentManager.findFragmentByTag("notificationsFragment") != null) {
// If the fragment exists, show it (no reason to recreate it).
fragmentManager.beginTransaction()
.show(fragmentManager.findFragmentByTag("notificationsFragment"))
.commit();
} else {
// If the fragment does not exist, add it to fragment manager with a tag to identify it.
fragmentManager.beginTransaction()
.add(R.id.content_frame, new NotificationsFragment(), "notificationsFragment")
.commit();
}
// Set the title
mToolbarTitleTextView.setText(R.string.title_activity_notifications);
}
mDrawerLayout.closeDrawer(GravityCompat.START);
return true;
}
// Useful method to hide the currently visible fragment
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;
}
EDIT: It is really hard to reproduce this error which makes it hard to debug. It seems to randomly happen.
Why hide and keep all the fragments with fragmentManager.beginTransaction().add(); you can avoid this error by keeping only one fragment in memory and avoiding the hassle of hiding fragments by using fragmentManager.beginTransaction().replace() and using the fragment lifecycle methods to store the fragment state if necessary.
Here is how I solved the problem. In my MainActivity I did this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
setContentView(R.layout.activity_main);
}
Basically what was happening is if I had 1+ fragments on the screen, if the android system ran low on resources while the app was in the background and shut it down, when restored, MainActivity.onCreate() would be called and it would re-instantiate all the fragments with the call
super.onCreate(savedInstanceState);
So I just made it null and this prevents from all those fragments to be recreated.
The reason they are overlapping is because they were all getting shown at once.
Definitely not the correct way to do it, but it solves my problem right now =P

Removing a fragment and then re-adding it

I've written a method, setLoading in my activity that is responsible for setting the "loading" status of my app. This method is responsible for instantiating a LoadingFragment, removing any existing instances of it (Using FragmentManager) and then depending on it's first parameter loading, adding it to one of two possible containers (Depending on the top parameter).
protected LoadingFragment loadingFragment;
public void setLoading(boolean loading, boolean top) {
FragmentManager fragmentManager = getFragmentManager();
// Create LoadingFragment instance if it has not already been created
if (loadingFragment == null || !(loadingFragment instanceof LoadingFragment)) {
loadingFragment = new LoadingFragment();
}
// Remove the fragment first if it is present
fragmentManager
.beginTransaction()
.remove(loadingFragment)
.commit();
// Only if loading is true should we display the fragment
if (loading) {
// Decide which container we're going to put the fragment in
int id = top ? R.id.topContainer : R.id.container;
// Place the fragment in the right position
fragmentManager
.beginTransaction()
.add(id, loadingFragment)
.commit();
}
}
public void setLoading(boolean loading) {
setLoading(loading, true);
}
I am triggering setLoading(true) from elsewhere in my activity and I have commented out it's corresponding setLoading(false) while testing.
What I want to happen is for my LoadingFragment to appear every time setLoading(true) is called. The first call shouldn't remove anything since it at that point it doesn't exist. All subsequent calls should remove the existing LoadingFragment and add it again.
What happens is that the first call to setLoading(true) does indeed create the LoadingFragment and put it in the correct container. However, subsequent calls to setLoading(true) remove the fragment, but it never seems to be re-added. I have checked to see that the fragment does indeed exist and is of type LoadingFragment at the point it is added and I have also checked to ensure that it's onCreateView method is being called.
Am I doing something wrong?
Edit
Using the answer given below by H Raval as a base I have now come up with the following:
public void setLoading(boolean loading, boolean top) {
FragmentManager fragmentManager = getFragmentManager();
Fragment currentLoadingFragment = fragmentManager.findFragmentById(R.id.loadingFragment);
if (currentLoadingFragment != null) {
fragmentManager
.beginTransaction()
.remove(currentLoadingFragment)
.commit();
}
if (loading) {
int id = top ? R.id.topContainer : R.id.container;
fragmentManager
.beginTransaction()
.add(id, new LoadingFragment())
.commit();
}
}
This seems to work as expected. It seems that the primary difference is that this code is creating a new LoadingFragment instance each time (When loading = true) whereas originally I was trying to use the same instance and just adding/removing it using the FragmentManager.
Out of interest, is there a reason I need to create a new instance after using remove? Is this the correct way to do it? Or should it still work when using the same instance? Additionally, if it's recommended to create a new instance each time, is there anything I should do in terms of clean-up, freeing up resources etc. (Perhaps there's a way of gracefully destroying the obsolete instances)?
well i have made some changes in your code and works perfect for me..let me know if you face any difficulty
public void loadFragment(boolean loading, boolean top){
FragmentManager fragmentManager = getSupportFragmentManager();
loadingFragment = new LoadingFragment();
// Only if loading is true should we display the fragment
if (loading) {
// Decide which container we're going to put the fragment in
int id = top ? R.id.topContainer : R.id.container;
if(top){
if(fragmentManager.findFragmentByTag("loadingFragment")!=null)
fragmentManager.beginTransaction().remove(fragmentManager.findFragmentByTag("loadingFragment")).commit();
fragmentManager
.beginTransaction()
.replace(R.id.topContainer, loadingFragment,"toploadingFragment")
.commit();
}else{
if(fragmentManager.findFragmentByTag("toploadingFragment")!=null)
fragmentManager.beginTransaction().remove(fragmentManager.findFragmentByTag("toploadingFragment")).commit();
fragmentManager
.beginTransaction()
.replace(R.id.container, loadingFragment,"loadingFragment")
.commit();
}
}

Fragment Transaction load empty view but fragment is shown after rotating device

I am building a navigation drawer as designed by the google documentation however I have an issue where the fragment is not being replaced. http://developer.android.com/training/implementing-navigation/nav-drawer.html
When the app first loads, the default fragment is loaded.
Clicking on another item on the drawer list leaves an empty view
However on rotating the device, loads the fragment chosen.
public void selectNavActivty(int position){
// TODO Changing between the different screens selection
fragment = null;
switch (position) {
case 0:
fragment = OverLay.newInstance();
break;
case 1:
fragment = Dummy.newInstance();
break;
}
if(fragment != null) {
// attach added to handle viewpager fragments
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.replace(R.id.content_frame, fragment).attach(fragment)
.addToBackStack(null);
trans.commit();
getFragmentManager().executePendingTransactions();
} else {
Log.d("Drawer Activity","Error in creating Fragment");
}
}
For navigation menu fragment transactions I use the following approach, this way the fragment will be added and placed on top.
String name = "myFragment";
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_frame, fragment, name)
.commit();
Look up the attach() function. It follows a different fragment lifecycle.
Also make sure that your layout files framelayout is visible.
Modify your code as below:
if(fragment != null) {
// attach added to handle viewpager fragments
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.replace(R.id.content_frame, fragment);
trans.addToBackStack(null);
trans.commit();
} else {
Log.d("Drawer Activity","Error in creating Fragment");
}
If the solution doesn't work for you, share the xml code along with your fragment code.
After adding Fragment it will be added to activity state and its view
will be added to defined Container view. But by attaching nothing will
be displayed if fragment was not already added to UI. It just attaches
to fragment manager. However if view was already added to a container
in UI and detached after that, by attaching it will be displayed again
in its container. Finally you can use attach and detach if you want to
destroy fragment View temporarily and want to display and build its
view on future without losing its state inside activity.
https://stackoverflow.com/a/18979024/3329488
My solution is to tag all the fragment with unique tag on fragment replacement. Make sure you also assign a unique tag to the default fragment during it creation. A more efficient way is to identify the fragment before you recreate the same one.
public void selectNavActivty(int position){
// TODO Changing between the different screens selection
FragmentManager fragmentManager = getSupportFragmentManager();
fragment = fragmentManager.findFragmentById(R.id.content_frame);
String fragmentTag = null;
switch (position) {
case 0:
fragmentTag = "case0Tag"; // please change to better tag name
break;
case 1:
fragmentTag = "case1Tag"; // please change to better tag name
break;
default:
Log.d("Drawer Activity","Error in creating Fragment");
return;
}
if (fragmentTag != null && !fragment.getTag().equals(fragmentTag))
fragmentManager.beginTransaction().replace(R.id.content_fragment, fragment, tag).commit();
}
In my case after rotating a device a blank fragment was shown. I understood that in an Activity.onCreate() I always called creating a blank Fragment and after that a needed one. So I changed it's behaviour to this:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
openEmptyFragment()
openAnotherFragment()
}
}
I recommend to check savedInstanceState != null before adding new fragments, as written in Why won't Fragment retain state when screen is rotated?.

How do I get the currently displayed fragment?

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.

Categories

Resources