I have created 3 Fragments namely (FragmentA, FragmentB, FragmentC) and one MainActivity.
There is a button in each fragment which replaces itself with next Fragment till FragmentC.
I am replacing FragmentA (with) FragmentB (then with) FragmentC.
Transaction from FragmentA to FragmentB uses below function
#Override
public void fragmentreplacewithbackstack(Fragment fragment, String tag) {
FragmentManager fragmentManager=getSupportFragmentManager();
FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.contner,fragment , tag);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
// fragmentManager.executePendingTransactions();
}
Transaction from FragmentB to FragmentC uses below function
public void fragmentreplace(Fragment fragment,String tag){
FragmentManager fragmentManager=getSupportFragmentManager();
FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.contner,fragment , tag);
fragmentTransaction.commit();
}
problem is when i press back button from FragmentC, FragmentC and FragmentA overlap with each other.
You need to add Fragment C to backstack as well if you wanna go to Fragment B on back press from here.
So call the below for Fragment C as well.
fragmentTransaction.addToBackStack(null);
EDIT - Change this current method you are using to go from B to C,
public void fragmentreplace(Fragment fragment,String tag){
FragmentManager fragmentManager=getSupportFragmentManager();
FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.contner,fragment , tag);
fragmentTransaction.addToBackStack(null); //this will add it to back stack
fragmentTransaction.commit();
}
I had the same problem and this answer of Budius helped me a lot.
You can solve your issue in the following way:
1) Instantiate the following listener (you have to keep reference of the FragmentC's instance) and, since fragmentA is the only fragment added to the backstack, when the user presses the back button, the number of transactions in the backstack will be zero
private OnBackStackChangedListener backStackChangedListener = new OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if(getSupportFragmentManager().getBackStackEntryCount()==0) {
if(fragmentC!=null) {
getSupportFragmentManager().beginTransaction().remove(fragmentC).commit();
}
}
}
};
2) Add the listener in the MainActivity when the latter is started
getSupportFragmentManager().addOnBackStackChangedListener(backStackChangedListener);
3) Remove the listener when the activity is stopped
why don't you just make a fragment activity with a frame layout in which you can replace that frame with any fragment..
Screen extends FragmentActivity
{
protected void onCreate(Bundle b)
{
super.onCreate(b);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.work);
callPerticularScreen(screenNumber,false,null,null);
}
public void callPerticularScreen(int screenNumber,boolean addToBackStack,String nameTag,Bundle b)
{
switch(screenNumber)
{
case registrationScreen:
callFragForMiddleScreen(registrationPageFrag,addToBackStack,nameTag);
break;
case dashboardScreen:
callFragForMiddleScreen(dashboardFrag,addToBackStack,nameTag);
break;
default:
break;
}
}
}
now from any fragment screen you can call this function to replace your fragment with another..like..
private void callFragForMiddleScreen(Fragment frag,boolean addToBackStack,String nameTag)
{
transFragMiddleScreen=getFragManager().beginTransaction();
transFragMiddleScreen.replace(getMiddleFragId(),frag);
if(addToBackStack)
{
transFragMiddleScreen.addToBackStack(nameTag);
}
transFragMiddleScreen.commit();
//getFragManager().executePendingTransactions();
}
from your fragement just call
Frag1 extends Fragment
{
onActivityCreate()
{
middleScreen=(Screen) getActivity();
middleScreen.callPerticularScreen(1,true,"tag");
}
}
layout for fragment activity..
<RelativeLayout
android:id="#+id/rl2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/rl" >
<FrameLayout
android:id="#+id/middle_screen"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
</RelativeLayout>
just don't add the fragment B to back stack so that you can come from c-> a directly.
25 support library
for example if you have
A -> B -> C
A id is 0
B id is 1
C id is 2
to rollback to A fragment just call
getFragmentManager().popBackStackImmediate(0 /* ID */, 0 /* flag */);
0 - is ID of backstack entry to rollback
or
popBackStackImmediate(1, FragmentManager.POP_BACK_STACK_INCLUSIVE)
FragmentManager.POP_BACK_STACK_INCLUSIVE, it means you want also remove supplied ID
also you can call
getFragmentManager().popBackStackImmediate("stack_name", 0)
"stack_name" it's a name of backstack to revert
for example
A -> B -> C -> D
A in MAIN_BACK_STACK
B in MAIN_BACK_STACK
C in SEPARATE_BACK_STACK
D in SEPARATE_BACK_STACK
if you want to revert to fragment B
just call
getFragmentManager().popBackStackImmediate("MAIN_BACK_STACK", 0)
or
getFragmentManager().popBackStackImmediate("SEPARATE_BACK_STACK", FragmentManager.POP_BACK_STACK_INCLUSIVE)
Related
Popbackstack is working fine when all the fragments in the sequence are added in the backstack but isnt working when one of the transactions is not added in the backstack.
Here is my navigation:
1.Replace fragment to load home fragment. This transaction not added to backstack.
Replace fragment to load login fragment. This transaction is added to backstack.
3.Replace fragment to load loggedin fragment. This transaction is not added to backstack.
Now, when i press back button once nothing happens. Whereas ideally it should go to the home fragment from logged in fragment.
Here is my onbackpressed method in main activity:
#Override
public void onBackPressed() {
if(getSupportFragmentManager().getBackStackEntryCount()>0)
{
FragmentManager.BackStackEntry backStackEntry = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1);
String str = backStackEntry.getName();
FragmentManager fm=getSupportFragmentManager();
//getSupportFragmentManager().popBackStackImmediate();
fm.popBackStack(str, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
else {
super.onBackPressed();
}
}
popBackstack only 'pop' what is in the backstack.
Since you haven't add the transaction when replacing the LoginFragment by the LoggedInFragment when you press back:
the LoggedInFragment remains,
the LogInFragment is popped
the HomeFragment is displayed
But because the LoggedInFragment as been added after the HomeFragment, the HomeFragment is displayed underneath it. So you can't see it as hidden by the LoggedInFragment.
One solution is to add the transaction to the back stack when you replace the LogInFragment by the LoggedInFragment.
Then in onBackPressed you test if the current fragment is the LoggedInFragment. If it's the case you pop the back stack up to HomeFragment (not inclusive). Like that both LoggedInFragment and LogInFragment will be pop.
EDIT
#Override
public void onBackPressed() {
FragmentManager manager = getSupportFragmentManager();
Fragment fragment = manager.findFragmentById(R.id.my_fragment_container);
// If there is something in the back stack AND the current fragment is the LoggedInFragment
if (manager.getBackStackEntryCount() > 0
&& fragment instanceof LoggedInFragment) {
manager.popBackStack(HomeFragment.class.getSimpleName(), 0);
} else {
super.onBackPressed();
}
}
In order to retrieve the HomeFragment by name you need to tag your transaction when you replace the current fragment by the HomeFragment. Generally I tag all transactions with the fragment's class simple name so like that I can retried any fragment:
transaction.replace(R.id.my_fragment_container, fragment, fragment.getClass().getSimpleName());
Eselfar's explanation of the problem is correct, but the code he provided wasn't generic enough for me.
I (hopefully) resolved this issue in a general case by the following code:
#Override
public void onBackPressed() {
Fragment currentFragment = getCurrentFragment();
if (mFragmentManager.getBackStackEntryCount() > 0) {
// In a normal world, just popping back stack would be sufficient, but since android
// is not normal, a call to popBackStack can leave the popped fragment on screen.
// Therefore, we start with manual removal of the current fragment.
removeCurrentFragment();
mFragmentManager.popBackStack();
} else {
super.onBackPressed();
}
}
private Fragment getCurrentFragment() {
return mFragmentManager.findFragmentById(getContentFrameId());
}
private void removeCurrentFragment() {
FragmentTransaction ft = mFragmentManager.beginTransaction();
ft.remove(getCurrentFragment());
ft.commit();
// not sure it is needed; will keep it as a reminder to myself if there will be problems
// mFragmentManager.executePendingTransactions();
}
I have 2 Activites A & B, In Activity A I managed fragment back stack but in one case as like below:
I have 4 fragments in Activity going from fragment 1 to 2 to 3 to 4 then from 4th fragment I'm going to Activity B now what i want is on press of back button fragment 4 of Activity should be opened and then on back fragment 3 and so on.
I'm using following function to replace the fragment and manage back stack
private void changeFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
fragmentManager.beginTransaction().replace(R.id.act_home_fl_container, fragment).commit();
}
On back press
boolean isPopFragment = getSupportFragmentManager().getBackStackEntryCount() > 0 ? true : false;
if (isPopFragment) {
getSupportFragmentManager().popBackStack();
}
I got success upto here but when I'm going to Activity B from 4th fragment and coming back to Activity A, I'm not able to manage back stack of fragments of Activity A.
I'm not having any how to start with it.
you can override onBackPressed inside your activities, check which fragment is on top and decide what to do as you wish:
#Override
public void onBackPressed()
{
// code here
super.onBackPressed(); // check if you need this, depends on your needs
}
Use this code to change fragment
public static void addFragment( final Fragment newFragment, final Fragment hideFragment) {
final FragmentManager fragmentManager = getFragmentManager();
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.hide(hideFragment);
fragmentTransaction.add(R.id.activity_home_container, newFragment, newFragment.getClass().getSimpleName());
fragmentTransaction.addToBackStack(hideFragment.getClass().getSimpleName());
fragmentTransaction.commitAllowingStateLoss();
}
newFragment "Fragment you want to add"
hideFragment "Fragment which is on container"
just use addToBackStack(tag_name) before commit
You can add en extension function in kotlin
fun AppCompatActivity.replaceFragment(containerId: Int, fragment: Fragment) =
supportFragmentManager.beginTransaction()
.replace(containerId, fragment, fragment.javaClass.name)
.addToBackStack(fragment.javaClass.name).commit()
i have app like this
one activity and inside it >
fragment a (loaded when run app also from menu can open it )
fragment b (open it from just menu)
fragment c (can open it from fragment a and also can open it from menu)
also inside fragment c there are 4 child fragments
in main activity(using navigation drawer as source) i call fragment a in oncreate like this
FragmentManager fragmentManager=getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.fragment_place,new First_Fragment()).addToBackStack("First").commit();
my problem is how to control back button to always back to fragment a and when fragment a is open close app
i was using addToBackStack(null) but is not what i want because will show all history of fragments that i opened
When adding fragment a to the back stack "addToBackStack(String name)" pass in a name.
Then listen for on back presses in your fragments
FragmentManager fm = getFragmentManager();
fm.addOnBackStackChangedListener(new OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
}
});
make sure to stop listening when each fragment is not being shown.
Then you can pop back to the named fragment added to the back stack
FragmentManager fm = getActivity()
.getSupportFragmentManager();
fm.popBackStack ("name", FragmentManager.POP_BACK_STACK_INCLUSIVE);
Make sure the rest of your fragment transactions are not added to the back stack. This should give you the behavior you want.
addToBackStack(String tag) is used to add the fragment to backstack and it contains the string as parameter. This parameter can be null or have some value.
If you pass null, it will add your fragment to backstack with tag null. addToBackStack(null) doesn't mean that your fragment is not added to backstack.
If you want your fragment will not be added to backstack, then just delete this line.
If you are adding your fragment to backstack and want it to be visible onBackPressed, then you can use
getSupportFragmentManager().popBackStackImmediate(/* Fragment TAG */,0);
CODE:- Try the below code and let me know.
Copy the below function in your main Activity.
/**
* function to show the fragment
*
* #param name fragment to be shown
* #param tag fragment tag
*/
public void showFragment(Fragment name, String tag) {
FragmentManager fragmentManager = getSupportFragmentManager();
// check if the fragment is in back stack
boolean fragmentPopped = fragmentManager.popBackStackImmediate(tag, 0);
if (fragmentPopped) {
// fragment is pop from backStack
} else {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, name, tag);
fragmentTransaction.addToBackStack(tag);
fragmentTransaction.commit();
}}
Show fragment using the below code.
showFragment(yourFragment, yourFragmentTag);
In mainActivity onBackPressed.
#override
public void onBackPressed(){
FragmentTransaction fts = getSupportFragmentManager().beginTransaction();
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager.getBackStackEntryCount() >= 2) {
// always show your fragment a here
showFragment(new FragmentA(), FragmentA.class.getSimpleName());
} else {
// finish your activity
}
}
I have these methods for fragment manipulation:
public void replaceFragment(Fragment fragment) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.main_frame, fragment);
fragmentTransaction.commit();
fragmentManager.executePendingTransactions();
}
public void addFragment(Fragment fragment, boolean addToBackStack) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.main_frame, fragment);
if (addToBackStack) {
fragmentTransaction.addToBackStack(fragment.getClass().getName());
}
fragmentTransaction.commit();
fragmentManager.executePendingTransactions();
}
public void clearFragments() {
if (fragmentManager.getBackStackEntryCount() > 0) {
FragmentManager.BackStackEntry first = fragmentManager.getBackStackEntryAt(0);
fragmentManager.popBackStack(first.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
Scenario:
I start with fragment A, when activity is created. addFragment(new A, false);
Then I add fragment B. replaceFragment(new B);
I add another fragment C. addFragment(new C);
In C fragment I want to reset application to A fragment. I call clearFragments(); addFragment(new A, false);
The problem is that B fragment onCreateView is called after pressing button on C fragment that clears fragments and adds A fragment. I don't see the fragment, but it starts background thread that crashes my app. I need to clear all fragments without calling their onCreateView() methods.
Is this expected behaviour, or am I doing something wrong?
Actually in such a scenario you will always return to the fragment B.
Step by step:
Adding A.
Replacing A with B <- after this step you don't have A within your activity.
Adding C. Here you are adding another fragment and telling FragmentManager to preserve current state of fragments to the back stack. Your current state at this point is fragment B. Then fragment C is added.
When you call clearFragments, FragmentManager returns to the first saved state - and this state is fragment B.
Maybe you forgot to mention something? I tried to put several entries in a back stack and then reset to the first state - onCreateView is called once, only for the first fragment.
I have one activity and multiple fragments in my app. i want to back one by one fragments when pressing back button which in all fragments.
i used this code segment but when pressing back button it comes to main activity without back one by one. Also i want to change the icon when it's comes to the main activity.(msg_alert)
btnBack.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
FragmentManager fm = MainActivity.this
.getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
Fragment fragment = new MainMenuLayout();
ft.replace(R.id.activity_main_content_fragment, fragment);
ft.commit();
btnBack.setVisibility(View.VISIBLE);
btnBack.setImageResource(R.drawable.msg_alert);
tvTitle.setText("Layout 0");
}
});
Lets say you are having two fragments A and B. Fragment A is is attached at the startup of activity and on any user event you navigate to fragment B by replacing the Fragment A.
1) while adding Fragment B to the activity
// Works with either the framework FragmentManager or the
// support package FragmentManager (getSupportFragmentManager).
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, fragmentB, "detail")
// Add this transaction to the back stack
.addToBackStack()
.commit();
2) override the onBackPressed of the activity to handle the back button press event.
#override
public void onBackPressed() {
// is there any fragment in backstack, if yes popout.
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
return;
}
super.onBackPressed();
}
this is one more option ,
in the Activity.
Fragment secondfragment= new SecondFragmnet();
#Override
public void onBackPressed() {
if(secondfragment.isVisible()){
// replace 1st fragment
}else{
// Alert dialog for Exit App
}
// you can check multiple fragments.
Hope it helps you
You have to Use addToBackStack() method while doing Transaction Between Fragments...
Read This : Implement Back Navigation for Fragments
Use Following code when you use Fragment Transaction...
String backStateName = fragment.getClass().getName(); // getting Fragment Name..
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame, fragment)
.addToBackStack(backStateName) //adding it to BackStack..
.commit();
In Fragments we dont have back navigation..
We can acheive that through replace() method.
I seen in your code that you are only replace the content frame. and so the result is backpress it comes to activity.
Also be sure that you have your mannual back button in the activity
this is test xml for activity
<?xml version="1.0" encoding="utf-8"?>
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Back" />
<FrameLayout
android:id="#+id/activity_main_content_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
Use
Fragment fragment = new MainMenuLayout();
ft.replace(R.id.activity_main_content_fragment, fragment);
ft.addToBackStack(null);// TODO parameter here it tag of fragment or null or "" String
ft.commit();
For more information about fragments backstack go through this android developer refrence :-Fragment BackStack