To show DialogFragment, I can use below two ways:
ProgressDialogFragment fragment = (ProgressDialogFragment) getFragmentManager().findFragmentByTag("progress_dialog");
if (fragment == null) {
fragment = ProgressDialogFragment.newInstance();
fragment.setCancelable(false);
getFragmentManager().beginTransaction()
.add(fragment, "progress_dialog")
.commitAllowingStateLoss();
}
or
ProgressDialogFragment fragment = (ProgressDialogFragment) getFragmentManager().findFragmentByTag("progress_dialog");
if (fragment == null) {
fragment.show(getFragmentManager().beginTransaction(), "progress_dialog");
}
When and which I should use? Or both are same?
there are pretty much the same. show retrieve a transaction, from the FragmentManager you provide as parameter, and call add/commit as you can see from the snippet
public void show(FragmentManager manager, String tag) {
mDismissed = false;
mShownByMe = true;
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commit();
}
the only difference is they reset some flags upon multiple calls of show()
Related
In my main activity I am displaying several fragments depending on option chosen by the user. When screen is rotated and then fragment changed, the previous fragment, one that was visible while rotating screen, is transparent and still visible behind or over new one. I am adding fragments using add() method and then removing old ones with remove(). I am aware that simply using replace() would fix my problem but my main fragment contains many network and database calls on create and I would like to remember its state rather than make many unnecessary calls.
Why does my problem even occur when I am removing fragments other than the main one? How can I fix it?
Here is how I am setting my fragments:
public void setFragment(FragmentEnum fragmentEnum, #Nullable Bundle bundle) {
Fragment oldFragment = getCurrentFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
currentFragment = fragmentEnum;
mMapFragment = (MainMapFragment) fragmentManager.findFragmentByTag(MainMapFragment.TAG);
if (mMapFragment == null) {
mMapFragment = MainMapFragment.newInstance(bundle);
}
if (fragmentEnum == FragmentEnum.MAP) {
if (!mMapFragment.isAdded()) {
ft.add(R.id.container_contents, mMapFragment, MainMapFragment.TAG);
}
if (oldFragment != null && !(oldFragment instanceof MainMapFragment)) {
ft.remove(oldFragment);
}
ft.show(mMapFragment);
if (mNavigationDrawerFragment != null) mNavigationDrawerFragment.unselectItems();
} else {
ft.hide(mMapFragment);
Fragment newFragment = null;
String tag = null;
switch (fragmentEnum) {
case LIST:
newFragment = MainListFragment.newInstance();
tag = MainListFragment.TAG;
break;
case FAVORITE:
newFragment = MainFavoriteFragment.newInstance();
tag = MainFavoriteFragment.TAG;
break;
case ADD:
newFragment = MainAddFragment.newInstance();
tag = MainAddFragment.TAG;
break;
case USER:
newFragment = MainUserFragment.newInstance();
tag = MainUserFragment.TAG;
break;
}
ft.add(R.id.container_contents, newFragment, tag);
if (oldFragment != null && !(oldFragment instanceof MainMapFragment)) {
ft.remove(oldFragment);
}
ft.show(newFragment);
}
ft.commit();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(SAVE_STATE_CURR_FRG, currentFragment);
}
onCreate(){
...
if (savedInstanceState != null) {
FragmentEnum savedFragmentEnum = (FragmentEnum) savedInstanceState.getSerializable(SAVE_STATE_CURR_FRG);
setFragment(savedFragmentEnum, null);
}
...
}
I am trying to talk to the fragments from activity.
Here in my MainActivity I am adding multiple fragments ok so for fine.
My main requirement is I don't want to add if fragment is already added.
So how can we check this condition?
Please help me some one.
code:-
private void intializingFragments(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
private View.OnClickListener intialization() {
return new View.OnClickListener() {
#Override
public void onClick(View v) {
int getId = v.getId();
if (getId == R.id.first) {
intializingFragments(new Fragment1());
} else if (getId == R.id.second) {
intializingFragments(new Fragment2());
} else if (getId == R.id.third) {
intializingFragments(new Fragment3());
}
}
};
}
You can use findFragmentByTag() or findFragmentById() functions to get a fragment. If mentioned methods are returning null then that fragment does not exist.
Fragment fragmentA = fragmentManager.findFragmentByTag("frag1");
if (fragmentA == null) {
// not exist
} else {
// fragment exist
}
for example:- http://wiki.workassis.com/android-load-two-fragments-in-one-framelayout/
You may find fragment in fragmentmanager:
List<Fragment> frags = getSupportFragmentManager().getFragments();
for (Fragment f : frags) {
<find what you want>...
}
Or you may add fragment with tag:
getSupportFragmentManager()
.beginTransaction()
.add(R.id.frame, new MyFragment(), "frag1")
.commit();
And find by tag
getSupportFragmentManager().findFragmentByTag("frag1");
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
Fragment topFragment = fragmentManager.findFragmentById(R.id.container);
FragmentA fragmentA = new FragmentA();
if(topFragment!= null)
{
transaction.remove(topFragment);
transaction.add(R.id.container, fragmentA, "FA");
transaction.commit();
}
else
{
transaction.add(R.id.container, fragmentA, "FA");
transaction.commit();
}
try this
private void intializingFragments(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment topFragment = fragmentManager.findFragmentById(R.id.container);
if(topFragment!= null)
{
fragmentTransaction.remove(topFragment);
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
else
{
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
}
How to check whether a fragment exists, without knowing its resource ID or tag:
// MyActivity.kt
val exists: Boolean = supportFragmentManager
.fragments
.filterIsInstance<MyFragment>()
.isNotEmpty()
There are many ways by which you can track the last added fragment. The simplest one is by finding the tag within fragment manager. Here is the sample code of it:
public boolean isFragmentPresent(String tag) {
Fragment frag = getSupportFragmentManager().findFragmentByTag(tag);
if (frag instanceof HomeFragment) {
return true;
} else
return false;
}
Alternatively, you can use your own variable to check whether the last added fragment is the same as the current fragment. Here is the sample code for it:
public boolean isCurrentFragment(Fragment fragment) {
if (fragment instanceof HomeFragment && getLastAddedFragment() instanceof HomeFragment) { // getLastAddedFragment() is a method which return the last added fragment instance
return true;
} else
return false;
}
And you can use it like:
if (isCurrentFragment(new HomeFragment())) {
// Last added Fragment is the HomeFragment
}
You can go for popBackStack Pop the last fragment transition from the manager's fragment back stack. If there is nothing to pop, false is returned. enter link description here
You can get the list of previously added fragments from the fragment manager. Then just check whether the list contains your fragment object or not.
getSupportFragmentManager().getFragments().contains(yourFragment);
This one doesn't work
!getSupportFragmentManager().getFragments().contains(fragment)
This works for me
getSupportFragmentManager().findFragmentByTag(fragment::class.java.getSimpleName()) == null
I have some fragments which will be replaced by following method. I think there's something wrong with my code because I want to prevent from adding multiple times a fragment into the back stack. If I click on fragment B twice, all instances will be added to the back stack and pressing back button will be passed through the two created instances.
public void replaceFragment(Fragment fragment, boolean addToBackStack, boolean customAnimation) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
String tag = fragment.getClass().getSimpleName();
if (customAnimation) {
transaction.setCustomAnimations(R.anim.slide_in_bottom, R.anim.slide_out_bottom, R.anim.slide_in_bottom, R.anim.slide_out_bottom);
}
transaction.replace(R.id.fragment_container, fragment, tag);
// remove from back stack if exists
// always return false!
boolean f = manager.popBackStackImmediate(tag, 0);
if (addToBackStack) {
transaction.addToBackStack(tag);
}
transaction.commit();
}
Keep it simple and just add to the back stack if needed.
If the Fragment being added is the same class as the current Fragment, don't add to the back stack:
public void replaceFragment(Fragment frag) {
FragmentManager manager = getSupportFragmentManager();
if (manager != null){
FragmentTransaction t = manager.beginTransaction();
Fragment currentFrag = manager.findFragmentById(R.id.content_frame);
//Check if the new Fragment is the same
//If it is, don't add to the back stack
if (currentFrag != null && currentFrag.getClass().equals(frag.getClass())) {
t.replace(R.id.content_frame, frag).commit();
} else {
t.replace(R.id.content_frame, frag).addToBackStack(null).commit();
}
}
}
try this on your activity onBackPressed method :
#Override
public void onBackPressed() {
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
if (fm.getBackStackEntryAt(fm.getBackStackEntryCount() - 1).getName().equals("your fragment tag")) {
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
I have something like this inside my activity:
#Override
public void onNavigationDrawerItemSelected(int position) {
Fragment fragment = null;
switch (position+1) {
case 1: {
fragment = new Fragment_Login();
FragmentManager frgManager = getFragmentManager();
frgManager.beginTransaction().replace(R.id.container, fragment)
.commit();
break;
}
case 2: {
SwipeRefreshListFragment swipeFragment = new Fragment_List_Of_Assessments();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.container, swipeFragment)
.commit();
break;
}
case 3: {
fragment = new Fragment_Report();
FragmentManager frgManager = getFragmentManager();
frgManager.beginTransaction().replace(R.id.container, fragment)
.commit();
break;
}
case 4: {
fragment = new Fragment_Settings();
FragmentManager frgManager = getFragmentManager();
frgManager.beginTransaction().replace(R.id.container, fragment)
.commit();
break;
}
default:
break;
}
}
The program automatically loads case1, but when case2 is selected, getSupportFragmentManager loads the Fragment on top of case1. I guess there are some problem with using both supportFragmentManager and FragmentManager. They seem to have their own stack. The problem is that I cannot use only either one of them because the SwipeToRefresh Android example uses ListView which needs support.v4.Fragment which needs the old FragmentManager. So how is it possible to integrate both FragmentManagers together?
I have accomplished something like this when using PreferenceFragment (not supported by support library version). In order to achieve this I kept inside my Activity a pair of boolean (isLastFragmentSupportType and lastFragmentShowed) and also a String (lastFragmentTag).
At the beginning your Activity will have both of them to false. And when you add a new Fragment you use these 2 boolean to know if you need to clean the other FragmentManager or not. I'll use your code as an example:
#Override
public void onNavigationDrawerItemSelected(int position) {
Fragment fragment = null;
switch (position+1) {
case 1: {
if(isLastFragmentSupportType && lastFragmentShowed)
{//As your last fragment was a support type you need to clear your supportFragmentManager
android.support.v4.app.Fragment fr_v4 = getSupportFragmentManager().findFragmentByTag(lastFragmentTag);
getSupportFragmentManager().beginTransaction().remove(fr_v4).commit();
}
fragment = new Fragment_Login();
FragmentManager frgManager = getFragmentManager();
frgManager.beginTransaction().replace(R.id.container, fragment,TAG1)
.commit();
lastFragmentTag = TAG1;
lastFragmentShowed = true;
isLastFragmentSupportType = false;
break;
}
//And so on with the others
You need to check what type (support or not) of fragment you are going to use, and check these variables to see if the last fragment was of a different type. If that is the case clean the other fragmentmanager to "clear" the screen so they wont overlap.
Also use TAGS to identify and retrieve your current fragments so you do not need to have Fragment variables over your code.
Finally use onSavedInstanceState so as to keep these values in case you need them.
Hope it helps :)
This answer is inspired from answer by zozelfelfo.
Use these two methods to replace fragments instead of getFragmentManager.beginTransaction.replace...
private void replaceFragment(Fragment fragment, String fragmentTag) {
if(lastFragmentShowed && isLastFragmentSupportType) {
android.support.v4.app.Fragment fr_v4 = getSupportFragmentManager().findFragmentByTag(lastFragmentTag);
getSupportFragmentManager().beginTransaction().remove(fr_v4).commit();
}
getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, fragment, fragmentTag)
.commit();
lastFragmentTag = fragmentTag;
lastFragmentShowed = true;
isLastFragmentSupportType = false;
}
private void replaceFragment(android.support.v4.app.Fragment fragment, String fragmentTag) {
if(lastFragmentShowed && !isLastFragmentSupportType) {
Fragment fr = getFragmentManager().findFragmentByTag(lastFragmentTag);
getFragmentManager().beginTransaction().remove(fr).commit();
}
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, fragment, fragmentTag)
.commit();
lastFragmentTag = fragmentTag;
lastFragmentShowed = true;
isLastFragmentSupportType = true;
}
I'm using BottomNavigationView as Tab Bar and switching fragments as tabs. All but one fragment are Support Fragments (and last one is PreferenceFragment). I'm using "hide-add-show" rather than "remove-replace". So, status of fragments in other tabs can be preserved.
Original function to switch:
private Fragment lastFragment = null;
private void switchFragment(Fragment fragment) {
if (lastFragment != fragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
if (null != lastFragment) {
transaction.hide(lastFragment);
}
lastFragment = fragment;
if (!fragment.isAdded()) {
transaction.add(R.id.fragment_container, fragment);
}
transaction.show(fragment).commitAllowingStateLoss();
}
}
I don't use tag nor boolean, just want to keep a reference to last fragment object. So, when switching, just call switchFragment() with instance of any fragment:
private Object lastFragment = null;
private void switchFragment(Object fragment) {
if (lastFragment != fragment) {
if (null != lastFragment) {
if (lastFragment instanceof android.support.v4.app.Fragment) {
hideFragment((android.support.v4.app.Fragment) lastFragment);
} else if (lastFragment instanceof android.app.Fragment) {
hideFragment((android.app.Fragment) lastFragment);
}
}
lastFragment = fragment;
if (fragment instanceof android.support.v4.app.Fragment) {
showFragment((android.support.v4.app.Fragment) fragment);
} else if (fragment instanceof android.app.Fragment) {
showFragment((android.app.Fragment) fragment);
}
}
}
So, this function still do the same things but switch between Support Fragment and Native Fragment by checking the target class. Helper functions:
// Support Version:
private void hideFragment(android.support.v4.app.Fragment fragment) {
getSupportFragmentManager().beginTransaction().hide(fragment).commit();
}
private void showFragment(android.support.v4.app.Fragment fragment) {
android.support.v4.app.FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
if (!fragment.isAdded()) {
transaction.add(R.id.fragment_container, fragment);
}
transaction.show(fragment).commitAllowingStateLoss();
}
// Native Version:
private void hideFragment(android.app.Fragment fragment) {
getFragmentManager().beginTransaction().hide(fragment).commit();
}
private void showFragment(android.app.Fragment fragment) {
android.app.FragmentTransaction transaction = getFragmentManager().beginTransaction();
if (!fragment.isAdded()) {
transaction.add(R.id.fragment_container, fragment);
}
transaction.show(fragment).commitAllowingStateLoss();
}
To avoid confusion, I removed the import so classes require full names.
I have a few fragments in my app, but my code opens a new fragment every time I click the button.
I want to know how can I change this, and make the fragment return to the exact same state I left it in.
The code im using right now:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragments);
MainActivity fragment = new MainActivity();
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.add(R.id.fragment_place, fragment);
transaction.commit();
turnGPSOn();
}
public void onSelectFragment(View view) {
if (view == findViewById(R.id.add))
{
newFragment = new Add();
}
else if (view == findViewById(R.id.map))
{
newFragment = new MainActivity();
}
else
{
newFragment = new Add();
}
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.replace(R.id.fragment_place, newFragment);
transaction.addToBackStack(null);
transaction.commit();
}
Thanks!
You are getting a new fragment each time because you are calling to new XXX() each time.
I think you could use findFragmentByTag in order to solve this problem. As you can see here the replace function can accept a third parameter that is a String, this String can be used as an id to identify different fragments you have used previously.
So to sum up you can:
Call Fragment f = getSupportFragmentManager().findFragmentByTag("FragAdd"); for example in order to retrieve the first fragment.
If f is null, that means that you haven't used that fragment yet, so you have to call to new Add() if not, use that fragment to replace the old one. For example like this:
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.replace(R.id.fragment_place, newFragment, "FragAdd"); //or whatever other string you want to use
transaction.addToBackStack(null);
transaction.commit();
Hope it helps :)
I faced this issue a time ago, and managed to solve it for applications with one visible fragment at a time; for activities with several visible fragments, you'll need to make some adjustments. This is what I did.-
Create a custom ParentActivity, so that all my activities extend it. ParentActivity knows about which is the current Fragment that is showed, and how to show a new one.
public String currentFragmentTag;
public ParentFragment getCurrentFragment(int fragmentWrapperResId) {
ParentFragment res = null;
FragmentManager fragmentManager = getSupportFragmentManager();
res = (ParentFragment) fragmentManager.findFragmentById(fragmentWrapperResId);
if (res != null && res.isHidden()) {
if (currentFragmentTag != null) {
res = (ParentFragment) fragmentManager.findFragmentByTag(currentFragmentTag);
}
}
return res;
}
public void openFragment(ParentFragment fragment, int fragmentWrapperResId, int enterAnim, int exitAnim, int popEnterAnim, int popExitAnim, boolean addToBackStack) {
FragmentManager fragmentManager = getSupportFragmentManager();
ParentFragment currentFragment = getCurrentFragment(fragmentWrapperResId);
if (currentFragment != null && currentFragment.getTagName().equals(fragment.getTagName())) {
return;
}
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim);
if (currentFragment != null) {
transaction.hide(currentFragment);
}
if (fragment.isAdded()) {
transaction.show(fragment);
} else {
transaction.add(fragmentWrapperResId, fragment, fragment.getTagName()).setBreadCrumbShortTitle(fragment.getTagName());
}
if (addToBackStack) {
transaction.addToBackStack(fragment.getTagName());
} else {
currentFragmentTag = fragment.getTagName();
}
transaction.commit();
}
Create a ParentFragment, to be extended by the rest of Fragments, with a tag getter
public String getTagName() {
return getClass().getSimpleName() + System.identityHashCode(this);
}
As you can see, the main idea is not replacing visible fragments, but just adding them and show/hide whenever it's needed. This way, the fragments will keep their states, as they're not destroyed until you remove them from the bakstack.