I have three fragments :
A is my main fragment, B is a login fragment, successful login will enter C fragment.
When I click on back button, I need to move to Fragment A from Fragment C.
My issue is that, when I click on back button I still move on Fragment B from Fragment A.
How can I fix my issue?
Here is my switch fragment function:
public void switchFragment(Fragment fragment) {
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.mainFrame, fragment, null);
transaction.addToBackStack(null);
transaction.commit();
}
A fragment(NewHomepage) to B(LoginFragment) fragment:
switchFragment(LogInFragment.newInstance());
This is my B fragment, it has the value logged to decide switch A fragment or not when it come from C fragment.
I think that issue must be here, when go back to A fragment and click back button want to quit the APP, I can see the logcat show 1=> and 2=> .
String logged = memberData.getUD_MBTYPENAME(); //get the value when login succeed
Log.d(TAG,"1=>"+logged);
//If UD_MBTYPENAME is not null,change to A fragment
if (!TextUtils.isEmpty(logged)) {
Log.d(TAG,"2=>"+logged);
((MainActivity) getActivity()).switchFragment(NewHomepage.newInstance());
}
Here is about my MainActivity about onKeyDown and switchFragment:
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (getSupportFragmentManager().getBackStackEntryCount() == 1) {
quickDialog();//It's a alert dialog
return false;
}
}
return super.onKeyDown(keyCode, event);
}
public void switchFragment(Fragment fragment) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.mainFrame, fragment, null);
transaction.addToBackStack(null);
transaction.commit();
}
Take a reference change the code like this , if I don't use transaction.addToBackStack(null); , the issue is still there , even though remove transaction.addToBackStack(null);
When I back to A , I have to click twice back to show the alert dialog, I don't know what happened when I click back first time in A fragment.
if (!TextUtils.isEmpty(logged)){
Log.d(TAG,"2=>"+logged);
hideFragment(LogInFragment.newInstance());
switchFragment(NewHomepage.newInstance());
}
public void switchFragment(Fragment fragment) {
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.mainFrame, fragment, null);
//remove it will fix my issue , but I have to click back twice to show alert dialog , I don't know what happened click it first time in A fragment.
//transaction.addToBackStack(null);
transaction.commit();
}
public void hideFragment(Fragment fragment) {
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.hide(fragment);
transaction.commit();
}
remove hideFragment and use manager.popBackStack(); on switchFragment , the issue will be fixed.
Try
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 ("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 ();
}
Related
Hi I have read this Difference between add(), replace(), and addToBackStack(). I have a confusion that If I add multiple fragments like below then If I press back button from fragment2 then will fragment1 will open ? If so then what is the use of addToBackStack as add already maintaining a stack.
FragmentTransaction ft = fragmentManager.beginTransaction();
Fragment fragment1 = new Fragment();
ft.add(R.id.llContainer, fragment1, "fragment_one");
Fragment fragment2 = new Fragment();
ft.add(R.id.llContainer, fragment2, "fragment_two");
ft.commit();
Well if you call multiple times add method on FragmentTransaction like this
FragmentTransaction ft = fragmentManager.beginTransaction();
Fragment fragment1 = new Fragment();
ft.add(R.id.llContainer, fragment1, "fragment_one");
Fragment fragment2 = new Fragment();
ft.add(R.id.llContainer, fragment2, "fragment_two");
ft.commit();
then both the fragments that been added to FragmentTransaction will be shown as overlapping.
Now clicking back will close the application. It won't start the previous fragment.
Hope this is what you were looking for.
Add method will not add your Fragment in BackStack. You need to verify once again.
While looking into code of addToBackStack
#Override
public FragmentTransaction addToBackStack(String name) {
if (!mAllowAddToBackStack) {
throw new IllegalStateException(
"This FragmentTransaction is not allowed to be added to the back stack.");
}
mAddToBackStack = true;
mName = name;
return this;
}
Flag mAddToBackStack = true; enabled which value is false by default. And this is the flag which is being used to add fragment into backstack. Have a look into below methods calls
#Override
public int commit() {
return commitInternal(false);
}
int commitInternal(boolean allowStateLoss) {
......
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
.....
}
So what you observed is not correct. Something you are missing
I have three fragments A、B、C , my process is A to B to C .
I want that when i change to C and call back to B , B can switch to A immediately.
So i set the switch fragment function on onResume on B .
But the result is that i always land up on A fragment , because when i change to B the function onResume be called
I know i can remove transaction.addToBackStack(null); then i can C to A
, but it will cause some bug in my project , so i try to keep it.
Is any function only start when C call back to B ?
Thanks in advance.
Below is my switch function:
private void switchFragment(Fragment fragment) {
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.mainFrame, fragment, null);
transaction.addToBackStack(null);
transaction.commit();
}
I try to add onResume on fragment B , but it will be A to B , and then B go back to A immediately.
#Override
public void onResume() {
super.onResume();
//go back to fragment A
switchFragment(NewHomepage.newInstance());
}
your fragment transition code should be:
private void switchFragment(Fragment fragment) {
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.mainFrame, fragment, null);
transaction.addToBackStack("fragment_name_tagX");
transaction.commit();
}
above, X is your fragment. After you navigate from fragment A to C using above function, your stack will have every fragment with name 'fragment_name_tag' that you assign.
#Override
public void onBackPressed() {
FragmentManager fm = getActivity().getSupportFragmentManager();
int count = fm.getBackStackEntryCount();
if (count != 0) {
fm.popBackStack ("fragment_name_tagB",FragmentManager.POP_BACK_STACK_INCLUSIVE);
} else {
super.onBackPressed();
}
}
So when you now press back button from fragment C, it will go on fragment A.
I am creating an application with multiple fragments. I have four fragments fragment1, fragment2, fragment3, fragment4. I am moving from different orders like f1 -> f2 -> f4 -> f3 -> f1 or any other order. But when I click the back button from each fragment I need to go to the previous fragment. How to handle this.
Edit 1:
I already tried
FragmentManager fm = ((Activity) context).getFragmentManager();
for (int i = 0; i < fm.getBackStackEntryCount(); i++) {
fm.popBackStack();
}
Which is not help me to solve my issue.
Sample code of Manage Fragment back stack
private Stack<Fragment> stack = new Stack<Fragment>();
public void pushFragments(Fragment fragment, boolean shouldAnimate,
boolean shouldAdd) {
drawerClose = false;
if (shouldAdd)
stack.push(fragment);
this.changeFragment = fragment;
invalidateOptionsMenu();
changeFragment(fragment, shouldAnimate, false);
}
public void popFragments() {
/*
* Select the second last fragment in current tab's stack.. which will
* be shown after the fragment transaction given below
*/
Fragment fragment = stack.elementAt(stack.size() - 2);
// / pop current fragment from stack.. /
stack.pop();
/*
* We have the target fragment in hand.. Just show it.. Show a standard
* navigation animation
*/
this.changeFragment = fragment;
invalidateOptionsMenu();
changeFragment(fragment, false, true);
}
private void changeFragment(Fragment fragment, boolean shouldAnimate, boolean popAnimate) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
if (shouldAnimate)
ft.setCustomAnimations(R.anim.slide_in_right,
R.anim.slide_out_left);
if (popAnimate)
ft.setCustomAnimations(R.anim.slide_in_left,
R.anim.slide_out_right);
ft.replace(R.id.content_frame, fragment);
ft.commit();
}
//On BackPress just check this thing
private void backManage() {
if (stack.size() > 1) {
popFragments();
}
}
Use addToBackStack(String tag), while committing the fragment to add the fragment into the stack of your application:
getFragmentManager().beginTransaction().replace(fragmentContainer.getID(), fragment)
.addToBackStack(null).commit();`
on Activity
#Override
public void onBackPressed() {
if(check_if_backstack_is_null)
super.onBackPressed();
else
{
popupFromBackstack();
}
}
You should override onBackPressed method:
#Override
public void onBackPressed() {
if (fragment != null && fragment.getChildFragmentManager().getBackStackEntryCount() > 0){
fragment.getChildFragmentManager().popBackStack();
}else {
super.onBackPressed();
}
}
For this you can set addToBackStack to fragment transation and then call commit.
By calling addToBackStack(), the replace transaction is saved to the back stack so the user can reverse the transaction and bring back the previous fragment by pressing the Back button.
If you add multiple changes to the transaction (such as another add() or remove()) and call addToBackStack(), then all changes applied before you call commit() are added to the back stack as a single transaction and the Back button will reverse them all together.
You just need to add addToBackStack(null) by FragmentTransaction.
when you are calling next Fragment just add this method with null parameter.
Like this.
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(..............);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Use this lines of code for it:-
#Override
public void onBackPressed() {
if ( getSupportFragmentManager().getBackStackEntryCount() > 0){
fm.popBackStack();
}else {
super.onBackPressed();
}
}
To get the backStack functionality in your fragmentthan you should have use the .addToBackStack(null) , while performing the fragment transaction like below:-
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.YOUR_CONTAINER, YOUR_FRAGMENT,"TAG")
.addToBackStack(null) /// IT IS NECESSARY TO GET THE BACK STACK PROPERTY IN YOUR FRAGMENT
.commitAllowingStateLoss();
Ok, I've stated that fragments are removed without reason. It seems to me, like this, but sureley there is a reason I don't know about. Please help me find it.
I got the FragmentActivity which contains TabHost, with 5 tabs. TabHost has a onTabChangedListener like this (nothing complicated here, just a case with copied code):
new OnTabChangeListener() {
#Override
public void onTabChanged(String arg0) {
LiveTabFragment fragment = null;
int viewHolder = -1;
switch (tabHost.getCurrentTab()) {
case TAB_ABOUT:
fragment = (LiveAboutFragment) getSupportFragmentManager().findFragmentByTag(LiveAboutFragment.class.getSimpleName());
if (fragment == null) fragment = new LiveAboutFragment();
viewHolder = R.id.live_tab_about;
break;
case TAB_EVENTS:
fragment = (LiveEventsFragment) getSupportFragmentManager().findFragmentByTag(LiveEventsFragment.class.getSimpleName());
if (fragment == null) fragment = new LiveEventsFragment();
viewHolder = R.id.live_tab_events;
break;
case TAB_PLAYERS:
fragment = (LivePlayersFragment) getSupportFragmentManager().findFragmentByTag(LivePlayersFragment.class.getSimpleName());
if (fragment == null) fragment = new LivePlayersFragment();
viewHolder = R.id.live_tab_players;
break;
case TAB_LEGEND:
fragment = (LiveLegendFragment) getSupportFragmentManager().findFragmentByTag(LiveLegendFragment.class.getSimpleName());
if (fragment == null) fragment = new LiveLegendFragment();
viewHolder = R.id.live_tab_legend;
break;
case TAB_CHAT:
fragment = (LiveChatFragment) getSupportFragmentManager().findFragmentByTag(LiveChatFragment.class.getSimpleName());
if (fragment == null) fragment = new LiveChatFragment();
viewHolder = R.id.live_tab_chat;
break;
}
if (fragment != null) injectInnerFragment(fragment, viewHolder, false);
}
}
Here is an injectInnerFragment method:
public void injectInnerFragment(AaFragment fragment, int viewHolderId, boolean addToBackStack) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
if (fragment.isAdded()) {
transaction.replace(viewHolderId, fragment, fragment.getClass().getSimpleName());
if (addToBackStack) transaction.addToBackStack(null);
transaction.commit();
} else {
transaction.add(viewHolderId, fragment, fragment.getClass().getSimpleName());
if (addToBackStack) transaction.addToBackStack(null);
transaction.commit();
}
}
Now the problem:
When I click on tab for thr first time fragment is beeing created (onCreate is called) which is normal. In most cases second clicking on already created tab fragment doesn't call fragment'sonCreate which is what I wanted. Thats why I'm trying to find fragment if FragmentManager first.
There are two cases it doesn't work, and previously created fragment is created again, which is not efficient for me. The cases are:
if I click on any tab, then on TAB_ABOUT, then clicking again on any tab causes it's recreating ("any tab fragment" is not found in FragmentManager)
if I click on any tab, then on TAB_CHAT, then clicking again on any tab causes it's recreating ("any tab fragment" is not found in FragmentManager)
What kind of sorcery is this? Is it some automatic memory freeing, dependant on fragment "weight"? Maybe I should store all data I don't want reload each time fragment is created on Activity instead?
Look at your following code :
transaction.replace(viewHolderId, fragment, fragment.getClass().getSimpleName());
try with :
transaction.add(viewHolderId, fragment, fragment.getClass().getSimpleName());
Solved this by modyfing inject method:
public void injectInnerFragment(AaFragment fragment, int viewHolderId, boolean addToBackStack) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
if (fragment.isAdded()) {
transaction.attach(fragment);
transaction.commit();
} else {
transaction.add(viewHolderId, fragment, fragment.getClass().getSimpleName());
if (addToBackStack) transaction.addToBackStack(null);
transaction.commit();
}
}
Im trying to load an new fragment when a method is called. This method creates a new fragment and "replaces" the other fragment:
private void showTestFragment(Fragment oldFragment, boolean addBackStack, BaseAdapter adapter, int position) {
Cursor cursor = (Cursor)adapter.getItem(position);
if(cursor != null){
int idx = cursor.getColumnIndexOrThrow(Episode._ID);
long rowId = cursor.getLong(idx);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
if(oldFragment != null){
Log.i(TAG, "Removing the old fragment");
fragmentTransaction.remove(oldFragment);
}
TestFragment testFragment = new TestFragment();
testFragment.setId(rowId);
fragmentTransaction.add(android.R.id.content, testFragment);
if(addBackStack){
Log.i(TAG, "Added to the backstack");
fragmentTransaction.addToBackStack(TAG);
}
fragmentTransaction.commit();
Fragment f = getFragmentManager()
.findFragmentById(R.id.index);
Log.i(TAG, "after commit, frag is "+ f);
}
}
This works fine, until i go back. The last fragment, should be removed when i go back. Before i'm going to implement methods on the activities
public void onBackPressed(){}
to remove the last fragment, i want to know if i handle the fragment change correctly. It looks like i'm missing something here..
If you really want to replace the fragment then use replace() methode instead of doing a remove() and an add().
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(..............);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Don't forget to do the addToBackStack(null) so your previous state will be added to the backstack allowing you to go back with the back button.
See also https://developer.android.com/reference/android/app/FragmentTransaction.html#replace(int, android.app.Fragment, java.lang.String) .
Another good source is http://developer.android.com/guide/components/fragments.html (search for replace() function).
Just remove first and the call super.onBackPressed
public void onBackPressed(){
// here remove code for your last fragment
super.onBackPressed();
}
//lets wait for systems back to finish
super.onBackPressed();
//here we believe a fragment was popped, so we need to remove the fragment from ourbackstack
if(fragmentBackStack.size()>0)
{
Log.d("custombackstack","before back: "+fragmentBackStack.size()+" current:"+fragmentBackStack.peek());
fragmentBackStack.pop();
}
//after popping is the size > 0, if so we set current fragment from the top of stack, otherwise we default to home fragment.
if(fragmentBackStack.size()>0)
{
Log.d("custombackstack","after back: "+fragmentBackStack.peek());
currentFragment = fragmentBackStack.peek();
}
else
{
//back stack empty
currentFragment = HOME_FRAGMENT;
}