I have an activity with a viewpager that contains 2 tabs. Each tab is a host fragment that has 2 child fragments. First fragments are recyclerviews that open up other fragments with FragmentTransactions. I am having trouble creating different backstacks for them.
So in my recyclerview adapter in onBindViewHolder i have the onclicklistener like this:
holder.image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FirstFragment fragment1 = new FirstFragment();
SecondFragment fragment2 = new SecondFragment();
FragmentManager fm = ((FragmentActivity) view.getContext()).getSupportFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
transaction
.replace(R.id.frame_container, fragment2,"Tag")
.addToBackStack(null)
.commit();
}
});
If i make the transaction like this from the recyclerview, the backstack is the same for both tabs, so if i open second fragments on tab1, then on tab2, if i go back to tab1 and press back, the second fragment on tab2 gets popped insead.
If i make a button in the FirstFragment outside of the recyclerview and make the fragment transaction from FirstFragment like this:
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.frame_container, SecondFragment.newInstance(), "Tag");
ft.addToBackStack(null);
ft.commit();
}
});
then it works as intended, but i want to do it from the recyclerview.
Is there any way this can be done?
You should carry your item click listener logic into the host fragment of the tab. Then in both of your host fragments you should make required transaction by using child fragment manager. Thus, the transactions are kept in the back stack of the host fragments. Then you have to manage these different stacks which are activity's fragment stack, host1's fragment stack and host2's fragment stack. For that purpose, you have to override onBackPressed in your activity then you ask the current visible host fragment for if there is a transaction. If there is you should pop that transaction and return in onBackPressed callback.If there is no let the activity to handle event by calling super.
Related
I'm creating a fragment which is supposed to act like a menu. I have successfully inflated it to the activity where I wanted it to be, however I now I realise that I cannot close the fragment. Furthermore I am able to scroll the contents of the activity which the fragment is placed over. How can I edit my code in such a way that the fragment will close after an action on the activity is detected, or one of it's contents is clicked?
I created the fragment by simply adding a fragment via new -> Fragment -> Fragment(Blank). I have not touched any of the code and have initialized the fragment like so in a on click:
findViewById(R.id.Menu).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_left);
MenuFragment menuFragment = new MenuFragment();
fragmentTransaction.replace(android.R.id.content, menuFragment);
fragmentTransaction.commit();
}
});
This is what it looks like, ignore the horrendous design.
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 a parent fragment and when i click in a button to open first fragment then click in button in first fragment to open a second fragment and from second fragment to third fragment and so on ....
i succeeded to do that but the application becomes very slow after launching multiple fragments
this is the code i used to launch any fragment
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view)
{
Fragment newFragment = new newFragment();
FragmentTransaction transaction =
getFragmentManager().beginTransaction();
transaction.replace(fragmentContainer.getId(), newFragment);
transaction.addToBackStack(null);
transaction.commit();
}
});
When you are calling android.support.v4.app.FragmentManager then you must use getSupportFragmentManager().
public FragmentManager getSupportFragmentManager ()
Return the FragmentManager for interacting with fragments associated
with this activity.
If your new fragments are not transparent, I think you should check the overdraw of layouts.
The replace() needs to recreate the instance and reload data, maybe add() and hide() can help you.
i should use getActivity().getSupportFragmentManager() instead of getFragmentManager()
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.
In my MainActivity in onCreate method I add() MainFragment to FrameLayout main_view_container:
if (savedInstanceState == null) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.main_view_container, new MainFragment(), MainFragment.FRAGMENT_TAG);
fragmentTransaction.commit();
}
In my MainFragment I have ViewPager with FragmentStatePagerAdapter and every page is Fragment itself (PagerFragment).
Then at some point after button click I want to replace whole MainFragment with another Fragment (ReplacementFragment) and add transaction to back stack so I can go back to MainFragment on back button pressed. So I do the following:
FragmentTransaction fragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.main_view_container, new ReplacementFragment(), null);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
After I commit transaction MainFragment is stopped and onDestroyView() is called but completely nothing happens with PagerFragments (that are in ViewPager in MainFragment) Not event a single onStop() call. Then when I go back MainFragment's View is recreated in onCreateView() which means new Instance of ViewPager as well, but still nothing with PagerFragments.
How is that possible that in parent Fragment onDestroyView is called but not in children Fragments?
You must use getChildFragmentManager() when creating pager adapter inside MainFragment.