Fragment Master-Details with saved State - android

In an activity there is a fragment that contains a list of objects. This fragment should be replaced by another fragment that contains details of a list item. When the detail fragment is active and the back button is pressed then the listfragment should be resumed.
my code:
#Override
public void onBackPressed() {
if (detailsFragment.isVisible())
{
goToListFragment();
}
}
}
public void goToListFragment() {
String backStateName = listFragment.getClass().getName();
FragmentManager fm = getSupportFragmentManager();
boolean fragmentPopped = fm.popBackStackImmediate(backStateName, 0);
if (!fragmentPopped){ //fragment not in back stack, create it.
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.main_fragment_container, listFragment);
ft.addToBackStack(backStateName);
ft.commit();
}
}
public void goToDetailsFragment(String foodAsJSON) {
String backStateName = detailsFragment.getClass().getName();
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_fragment_container, detailsFragment)
.addToBackStack(backStateName)
.commit();
}
what I observe:
listfragment: onPause()
<back pressed in details view>
listfragment: oncreateView()
listfragment: onResume()
So, when the back button is pressed I would expect that only the onResume function is called. Actually that how its described in the fragment lifecycle. But as I have observed the oncreateView() is called too. That causes the fragment to reload completely and that's not the wanted behavior. So, how do I resume the master fragment properly?

Related

Android fragment handling back button

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();

Back in fragment and nested fragment

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
}
}

Fragment Switching in the same Activity restarts the same Fragment

I am working on an application.
Somewhere I want to load a Fragment in an Activity and when a Gridview/Listview item will be clicked on the FirstFragment, I load the SecondFragment
I have added the Fragments the way below:
MainActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.form_creation_view);
Fragment fragment = new FirstFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.form_view, fragment).commit();
}
Second Fragment:
Fragment fragment = new SecondFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.addToBackStack(null);
fragmentTransaction.replace(R.id.form_view, fragment).commit();
OnBackPress of MainActivity.java :
#Override
public void onBackPressed() {
if(getFragmentManager().getBackStackEntryCount() == 0) {
super.onBackPressed();
} else {
getFragmentManager().popBackStack();
}
}
So, when I press the BackButton on the Mainactivity when SecondFragment is loaded, it switches to the FirstFragment, but it calls the onCreateView() method of the FirstFragment again.
The FirstFragment has a Recyclerview inside and I send an API request in the OnCreateView() method to the server in order to load the data into this Recyclerview.
Now when I come back again to this Fragment from the SecondFragment, I want the it so that the request should not be executed and the data should be populated into the RecyclerView.
How can I achieve this?
Can anyone please help me here?
in your onCreateView, you should do a check like this:
if(mList.size() == 0){
//make api call
}else{
//set recyclerView adapter
}
This way, when you come back from the SecondFragment you can repopulate the RecyclerView.
You should also consider saving this list on onSaveInstanceState

Backstack Fragment not appearing in front on Back button prerssed

I have gone through many stackoverflow question before writing this. i am so confused about this guy Backstack in fragment.
I have Added three fragment on the same container inside an Activity
Fragment 1 :
private void addLandingFragment() {
landingPageFragment = LandingPageFragment.newInstance();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add( R.id.container, landingPageFragment, LANDING_PAGE_FRAGMENT_TAG );
transaction.commit();
}
Fragment 2 :
public void addIntrofragment() {
fragment2 = IntroFragment.newInstance();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace( R.id.container, fragment2, INTRO_PAGE_FRAGMENT_TAG);
transaction.addToBackStack(fragment2.getClass().getName() );
transaction.commit();
}
Fragment 3 :
public void onGetStartedClicked() {
fragment3= ConnectFragment.newInstance();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace( R.id.container, fragment3,CONNECT_PAGE_FRAGMENT_TAG );
transaction.commit();
}
Now what I want is when user presses back button on fragment 3 it should come on very first fragment so I have overrided the onBackPressed() method.
#Override
public void onBackPressed() {
manager.popBackStack(fragment2.getClass().getName() ,FragmentManager.POP_BACK_STACK_INCLUSIVE );
}
but nothing happening on screen it keeps fragment 3 running.
UPDATE
When I am navigating from
fragment1 > fragment2
and presses back button on fragment2, I am coming to fragment1 but if move from
fragment1 > fragment2> fragment3
I am getting the stack entry count 1 on onBackPressed() method but on device screen it still shows fragment3. Now pressing back button again will exit me from app but fragment1 wont come on screen. So puzzling why it is happening ?
Any solution to achieve this.
calling replace() will remove the previous fragment, Fragment 1 should be called using replace(), and Fragment 2 & 3 should be called using add(), you should also add the last transaction to back stack (calling Fragment 3)
Like this:
Fragment 1:
private void addLandingFragment() {
landingPageFragment = LandingPageFragment.newInstance();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace( R.id.container, landingPageFragment, LANDING_PAGE_FRAGMENT_TAG );
transaction.commit();
}
Fragment 2:
public void addIntrofragment() {
fragment2 = IntroFragment.newInstance();
FragmentTransaction transaction = manager.beginTransaction();
transaction .hide(LandingPageFragment.this);
transaction.add( R.id.container, fragment2, INTRO_PAGE_FRAGMENT_TAG);
transaction.addToBackStack(fragment2.getClass().getName() );
transaction.commit();
}
Fragment 3:
public void onGetStartedClicked() {
fragment3= ConnectFragment.newInstance();
FragmentTransaction transaction = manager.beginTransaction();
fragmentManager.popBackStackImmediate(); // to remove fragment 2
transaction.add( R.id.container, fragment3,CONNECT_PAGE_FRAGMENT_TAG );
transaction.addToBackStack(fragment3.getClass().getName() );
transaction.commit();
}
finally your onBackPressed should be like this:
#Override
public void onBackPressed() {
fragmentManager.popBackStackImmediate();
fragmentTransaction.show(LandingPageFragment.this);
}
therefore your onBackPressed will always pop the top fragment on the stack (Fragment 3), and since Fragment 2 was already popped before adding Fragment 3, then onBackPressed will display the very first fragment.
Thanks Silvia.H for you support. I have solved my problem and found it as the best possible solution for me.
The only mistake I did was, I did not add fragment3 in backstack
So the only changes required was
public void onGetStartedClicked() {
fragment3= ConnectFragment.newInstance();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace( R.id.container, fragment3,CONNECT_PAGE_FRAGMENT_TAG );
transaction.addToBackStack(ConnectFragment.class.getName() );
transaction.commit();
}
Now this makes you clear that in order to use popBackStack with name like
manager.popBackStack(fragment2.getClass().getName() ,FragmentManager.POP_BACK_STACK_INCLUSIVE );
you have to keep that transaction in backstack from where you are actually pressing the back button.
I have made one more small change in onBackPressed() method which allows the app to exist when user presses back button on fragment1.
My onBackPressed() look like this now
#Override
public void onBackPressed() {
if( manager.getBackStackEntryCount() > 0 ) {
getSupportFragmentManager().popBackStack(
scoreTrackerIntroFragment.getClass().getName(),
FragmentManager.POP_BACK_STACK_INCLUSIVE );
}else {
super.onBackPressed();
}
}
Woop! Now I am clear about this "Backstack" guy.

android fragment handle back button press not working

I am working fragment, I replaced FragmentA by fragment B.it's working perfect,but when I press back button I can't back Fragment A.
I tried to override onKeyDown method,but I can't override this method in fragment
this is a my source
CategoryViewPager fragment1 = new CategoryViewPager();
FragmentTransaction ft = getSupportFragmentManager()
.beginTransaction().addToBackStack("method");
ft.setCustomAnimations(R.anim.trans_left_in, R.anim.trans_left_out);
ft.replace(R.id.content_frame, fragment1, "fragment2");
ft.commit();
when I use CategoryViewPager and click back button I can't go back(fragment wich i replaced this fragment)
how can I solve my problem?
Call addToBackStack() before you commit the transaction:
getSupportFragmentManager().beginTransaction()
// Add this transaction to the back stack
.addToBackStack()
.commit();
No need to use tag in your case. just use like this
/*
* Add this transaction to the back stack.
* This means that the transaction will be remembered after it is
* committed, and will reverse its operation when later popped off
* the stack.
*/
fragmentTransaction.addToBackStack(null);
It is better to handle back button press through your activity unless your actvity host large number of fragments.When you press back button inside your fragment onBackPressed() method of your activity will be called which can be overridden and handled..So handling back button for fragments can be done in this way..
MainActvity
public static boolean isMainActivityShown ;
public static boolean isFragment1Shown=false ;
public static boolean isFragment2Shown=false ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isMainActivityShown=true //inside onCreate method put isMainActivityShown true
.
.
.
}
.
.
Fragment currentFragment = new Fragment1();
isMainActivityShown=false; //when moving to fragment1
isFragment1Shown=true;
frgManager = getFragmentManager();
frgManager.beginTransaction().replace(R.id.content_frame, currentFragment)
.commit();
#Override
public void onBackPressed() {
if(isMainActivityShown)
{
finish();
}
else if(isFragment1Shown)
{
//write the code to handle back button when you are in Fragment1
}
else if(isFragment2Shown)
{ //When you are in Fragment 2 pressing back button will move to fragment1
Fragment currentFragment = new Fragment1();
isFragment2Shown=false;
isFragment1Shown=true;
FragmentManager frgManager;
frgManager = getFragmentManager();
frgManager.beginTransaction().replace(R.id.content_frame, currentFragment)
.commit();
}
}
Fragment1
Fragment currentFragment = new Fragment2();
MainActivity.isFragment1Shown=false;
MainActivity.isFragment2Shown=true;
frgManager = getFragmentManager();
frgManager.beginTransaction().replace(R.id.content_frame, currentFragment)
.commit();
Check this link
Check the addToBackStack(String name) method. This should solve your problem.

Categories

Resources