I'm trying to implement the first Activity of an app with the BottomNavigation. This will have three tabs whose will open three different fragments.
The landing fragment is a MapView then you can access to the other fragments so for it I replace them.
private void replaceFragment(Fragment fragment, String tag) {
FragmentManager fragmentManager = getFragmentManager();
if(fragmentManager.findFragmentByTag(tag) != null) {
fragmentManager.popBackStack(tag, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}else {
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.replace(R.id.framelayout_home_content, fragment, tag);
ft.addToBackStack(tag);
ft.commit();
}
}
Every time I replace my fragment and go back to the Map the map is built again. Is there any way to keep the Map or speed up the process in order to avoid flickr every time it is shown?
should I addToBackStack and then Pop It up?
Indeed I already tried it but I don't think I've done it right
private void showSearchMap() {
if (getFragmentManager().getBackStackEntryCount() == 0) return;
FragmentManager fm = getFragmentManager();
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
in order to avoid open a fragment once it is already visible:
if( getFragmentManager()
.findFragmentById(R.id.framelayout_home_content)
instanceof SettingsFragment ) return;
Is there any proper way to handle this BottomNavigation with Fragments?
Related
I am pretty new to android development so I am curious how to work properly with Fragments.
My application contains a BottomNavigationActivity which switches between 3 fragments with this code:
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_montage_order_detail, fragment).commit();
I am storing the Fragments in a List<Fragment> to avoid loosing the current state. But everytime I replace the fragment with another the method onDestroy() is called.
I know, I know I could add and remove the fragment in the fragmentmanager instead of replacing it. I googled alot and most of the tutorials tell me to replace the fragment.
Whats the common way to keep a fragments state without recreating it on every call?
Find the solution
It will not recreate fragment anytime
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().add(R.id.content_montage_order_detail, fragment).commit();
Use fragment TAG at time of creation of fragment then when you want to get it again use findFragmentByTag. if fragment already created then old one will be find by fragment manager.
Fragment previousFragment = fragmentManager.findFragmentByTag("TAG");
I suggest you use show,not forreplace
protected void addFragmentStack(Fragment fragment) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (this.mContent != fragment) {
if (fragment.isAdded()) {
ft.hide(this.mContent).show(fragment);
} else {
ft.hide(this.mContent).add(getFragmentViewId(), fragment);
}
this.mContent = fragment;
}
ft.commit();
}
Try using switchFragment to switch fragment, it will show fragment if it is already added.
Use fragmentTransaction.show method to re-use existing fragment i.e. saved instance.
public void switchFragment (Fragment oldFragment, Fragment newFragment, int frameId) {
boolean addFragment = true;
FragmentManager fragmentManager = getFragmentManager ();
String tag = newFragment.getArguments ().getString (BaseFragment.TAG);
Fragment fragment = fragmentManager.findFragmentByTag (tag);
// Check if fragment is already added
if (fragment != null && fragment.isAdded ()) {
addFragment = false;
}
// Hide previous fragment
String oldFragmentTag = oldFragment.getArguments ().getString (BaseFragment.TAG);
if (!tag.equals (oldFragmentTag)) {
FragmentTransaction hideTransaction = fragmentManager.beginTransaction ();
Fragment fragment1 = fragmentManager.findFragmentByTag (oldFragmentTag);
hideTransaction.hide (fragment1);
hideTransaction.commit ();
}
// Add new fragment and show it
FragmentTransaction addTransaction = fragmentManager.beginTransaction ();
if (addFragment) {
addTransaction.add (frameId, newFragment, tag);
addTransaction.addToBackStack (tag);
}
else {
newFragment = fragmentManager.findFragmentByTag (tag);
}
addTransaction.show (newFragment);
addTransaction.commit ();
}
Ya, you can also manage the state by managing the backstack.
Hi I am using a Stack variable for storing the sequence of fragments clicked by the user
For example : [A,B,C,A,D]
Now I need to check if a fragment exits in the stack I need to reuse it.
From the above example I need to reuse the first element i.e, A when user clicks on the fourth time.
The problem is I have a custom keyboard opening in fragment A, now if the same fragment is in the stack as forth element the keyboard is not opening .but when I backpress and go to first element keyboard is opened in the first instance of A
Code for storing in the Stack
public void switchContent(Fragment fragment) {
if(mContent !=null && (fragment.getClass().toString().equals(mContent.getClass().toString()))){
getSlidingMenu().showContent();
return;
}
mContent = fragment;
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.add(R.id.content_frame, mContent);
FragmentChangeActivity.fragmentStack.lastElement().onPause();
FragmentChangeActivity.fragmentStack.push(mContent);
ft.commit();
getSlidingMenu().showContent();
}
Code for backpress
#Override
public void onBackPressed() {
if (fragmentStack.size() >= 2) {
FragmentTransaction ft = fragmentManager.beginTransaction();
fragmentStack.lastElement().onPause();
ft.remove(fragmentStack.pop());
fragmentStack.lastElement().onResume();
ft.show(fragmentStack.lastElement());
ft.commit();
} else {
super.onBackPressed();
}
}
Any help is appreciated.
The following code check's if Fragment named SearchFragment exists or not in backstack and processes accordingly. You can use this logic to determine if a specific Fragment exists or not
FragmentManager fragmentManager=getSupportFragmentManager();
Fragment currentFragment = (Fragment)fragmentManager.findFragmentById(R.id.mainContent);
if(currentFragment!=null && currentFragment instanceof SearchFragment){
//SearchFragment exists in backstack , process accordingly
}else{
//SearchFragment not present in backstack
}
I am navigating from one fragment to another fragment but my title alone changes my screen remains same. In the background the new fragment is up and running. This happens only when i navigate during any api call in the background.
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
if (main_fragment_container != null) {
ft.replace(main_fragment_container.getId(), fragment, fragment.getClass().getName());
}
if (isStackNeeded) {
ft.addToBackStack(fragment.getClass().getName());
}
ft.commit();
My activity is composed of 3 nested Fragments. There is my MainFragment that is displayed by default, ProductFragment that can be called from it, then DetailFragment can be called from ProductFragment.
I can go back and forth between my ProductFragment and DetailFragment. By doing so, the popStackBack method is accumulating similar fragments. Then, if I click on the back button, It will go back through all the Fragments as many time I called them.
What is the proper way to avoid the same Fragment to be kept in the back stack ?
EDIT :
I firstly call my main fragment :
if (savedInstanceState == null) {
getFragmentManager()
.beginTransaction()
.add(R.id.container, new SearchFragment(), "SEARCH_TAG")
.commit();
}
Here is the code that calls the fragments from the activity :
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.setCustomAnimations(R.animator.enter_from_bottom, R.animator.exit_to_top, R.animator.enter_from_bottom, R.animator.exit_to_top);
ft.replace(R.id.container, new FactFragment(), "FACT_TAG");
ft.addToBackStack("FACT_TAG");
ft.commit();
Then, on back click :
#Override
public void onBackPressed() {
getFragmentManager().popBackStack();
}
I tried to get the tag of my current fragment and execute some specific code related to it but it doesn't work well. I also tried to addToBackStack() only when current Fragment wasn't already added to the backStack but it messed up my fragment view.
Use fragment's method isAdded() to evaluate the insertion. For example:
if(!frag.isAdded()){
//do fragment transaction and add frag
}
Here is my solution. Maybe dirty but it works. I implemented a method that returns the tag of the fragment that is displayed before clicking the on back button :
public String getActiveFragment() {
if (getFragmentManager().getBackStackEntryCount() == 0) {
return null;
}
String tag = getFragmentManager()
.getBackStackEntryAt(getFragmentManager()
.getBackStackEntryCount() - 1)
.getName();
return tag;
}
Then, on my onBackPressed() method :
// Get current Fragment tag
String currentFrag = getActiveFragment();
if(currentFrag.equals("PRODUCT_TAG")) {
// New transaction to first Fragment
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.setCustomAnimations(R.animator.enter_from_right, R.animator.exit_to_left, R.animator.enter_from_right, R.animator.exit_to_left);
ft.replace(R.id.container, new SearchFragment(), "MAIN_TAG");
ft.commit();
} else {
// Go to Fragment-1
getFragmentManager().popBackStack();
}
Here is my handy and simple solution to check for duplicate insertion through fragment manager
at first, I check if it is first time intention for adding fragment and then I check if the fragment is presented using fragment manager
Fragment fragment = getSupportFragmentManager().findFragmentByTag("firstFragment");
if (fragment == null) {
getSupportFragmentManager().beginTransaction().add(R.id.frameLayout, new FirstFragment(), "firstFragment")
.addToBackStack(null)
.commit();
}else if(!fragment.isAdded()){
getSupportFragmentManager().beginTransaction().add(R.id.frameLayout, new FirstFragment(), "firstFragment")
.addToBackStack(null)
.commit();
}
Here is my solution:
Fragment curFragment = fragmentManager.findFragmentById(R.id.frameLayout);
if(curFragment != null
&& curFragment.getClass().equals(fragment.getClass())) return;
// add the fragment to BackStack here
Xamarin.Android (C#) version:
var curFragment = fragmentManager.FindFragmentById(Resource.Id.frameLayout);
if (curFragment != null
&& curFragment.GetType().Name == fragment.GetType().Name) return;
// add the fragment to BackStack here
I have added 3 fragments to my Activity with
String name = "fragment1"; // and ..2 and ..3
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.content_frame, fragment, name);
fragmentTransaction.addToBackStack(name);
fragmentTransaction.commit();
The last added (third) Fragment now is visible on top.
Now I want to resume to the first added Fragment. But how?
I can find this Fragment with
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment firstFragment = fragmentManager.findFragmentByTag("fragment1");
If I call fragmentManager.getFragments() I still can find all three Fragments.
How to bring firstFragment back to top, make it visible again?
You can hide your 2nd and 3rd fragment and make your 1st fragment visible. So you'll have the effect that first fragment is shown on top and others are invisible.
solution:
Use the FragmentTransaction's show and hide method. Firs you need to find all the fragment and call the FragmentTransaction to show and hide 2nd and 3rd fragments.
Here's how I do it in my app when I want to switch to fragment:
FragmentTransaction transaction = getFragmentManager().beginTransaction();
if (fragment.isAdded())
{
transaction.show(fragment);
}
else
{
transaction.replace(R.id.container, fragment);
}
transaction.addToBackStack(null);
transaction.commit();
Note the use of show.
It ended up with this:
/**
* #param tag name of the fragment to resume and to bring to top
* #return true if fragment has been found
*/
private boolean bringFragmentToTop(String tag) {
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag(tag);
if (fragment != null) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
for (Fragment f : fragmentManager.getFragments()) {
if (f == fragment)
fragmentTransaction.show(f);
else
fragmentTransaction.hide(f);
}
fragmentTransaction.commit();
return true;
}
return false;
}