Managing the back button with nested fragment - android

I have a MainActivity with a content_container to load Fragments in it.
In one of the Fragment I also have a sub_container to load Fragments.
In the MainActivity i manage the back button with a stack of Fragment:
public Fragment addFragmentToStack(Fragment fragment){
if(fragmentStack.size()>MAX_NUM_FRAGMENTS){
fragmentStack.remove(0);
}
return fragmentStack.push(fragment);
}
#Override
public void onBackPressed() {
if (fragmentStack.size() > 0) {
fragmentStack.pop();
if (MyApplication.fragmentStack.size() > 0) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_container, fragmentStack.peek());
ft.commit();
}
}
}
and whenever I want to replace a Fragment in my content_container I use
Fragment someFragment = new SomeFragment();
fragmentTransaction.replace(R.id.content_container, ((MainActivity)getActivity()).addFragmentToStack(someFragment), "");
fragmentTransaction.commit();
One Fragment is a gallery, with a menu to load a photos or a videos Fragment in it (so the gallery is the parent Fragment and the photos and videos Fragments are children).
In order to be able to add the photo gallery and the video gallery in the stack, I use in the GalleryFragment:
if(mType.equals("photos")){
Fragment videoFragment = new VideoFragment();
fragmentTransaction.replace(R.id.sub_container, videoFragment, "");
((MainActivity)getActivity()).addFragmentToStack(this);
}
else{
Fragment photoFragment = new PhotoFragment();
fragmentTransaction.replace(R.id.sub_container, photoFragment, "");
((MainActivity)getActivity()).addFragmentToStack(this);
}
i.e. I add the parent GalleryFragment in the stack.
When I go from the videos to the photos gallery, for instance, and press the back Button, the code in MainActivity's onBackPressed is executed, and the current GalleryFragment instance is replaced by the instance in the stack, but its sub_container is empty (none of onCreateView or onResume is called).
Anybody can help?

Ok, adding the same instance to the stack was not a good idea. Instead, if I add a new instance, it works perfectly, i.e. :
instead of
((MainActivity)getActivity()).addFragmentToStack(this);
use
GalleryFragment fragment = new GalleryFragment();
Bundle bundle = new Bundle();
bundle.putString("type", "photos"); // or bundle.putString("type", "videos");
// in order to know what to load once in there
fragment.setArguments(bundle);
((MainActivity)getActivity()).addFragmentToStack(fragment);

Related

Android: Replace sub fragment with it's parent fragment in main activity container

Imagine i have main activity that has viewpager. I have 2 fragments called (F1 & F2) that will transaction into viewpager.
Again imagine in F1 fragment i want to set a button. When clicking on button, i want to transaction other fragment call SUBF1 but not into F1 fragment.
My question is here!!! Is it possible to replace SUBF1 with it's parent means F1?My idea is that i want to replace sub fragment with it's parent fragment that has been kept on fragment's container in main activity?
final FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.details, new NewFragmentToReplace(), "NewFragmentTag");
ft.commit();
You can save the instance of current fragment, when you are
navigating from one fragment to other. When user press the back
button, you can open the specific fragment with the help of tag.
#Override
public void onBackPressed() {
String currentTag = currentFragment.getTag();
if(currentTag.equals(res.getString(R.string.fragmentTagTest)))
{
currentFragment = AnotherFragment;
replaceFragment() // integrate replace current fragment with new one.
}
}

fragment transaction - pop backstack and then add fragment

I am trying to do the following use case in Android Fragments. I have 2 fragments.
Fragment A -> Fragment B
When a user does something in Fragment B, I want to have the back stack as follows
Fragment A -> Fragment C. So, when the user presses back I want the user to go back to Fragment A.
I have tried the following
mFragmentManager.popBackStackImmediate();
FragmentTransaction fragmentTransaction = fMgr.beginTransaction()
.replace(R.id.base, Fragment_C, "1")
.addToBackStack(null)
.commitAllowingStateLoss();
The problem here is that I can see Fragment A for a short period of time before Fragment C is shown
If I do the following
mFragmentManager.popBackStackImmediate();
FragmentTransaction fragmentTransaction = fMgr.beginTransaction()
.replace(R.id.base, Fragment_C, "1")
.addToBackStack(null)
.commitNowAllowingStateLoss();
I get the error
This transaction is already being added to the back stack
I can get Fragment C to show up if I do this BUT
mFragmentManager.popBackStackImmediate();
FragmentTransaction fragmentTransaction = fMgr.beginTransaction()
.replace(R.id.base, Fragment_C, "1")
.commitNowAllowingStateLoss();
This works and I don't see Fragment A and see Fragment C but the back button takes the user out of the application. So, is it possible that we can pop the back stack of the fragment and then add another fragment to the back stack w/o showing Fragment A AND the back button takes the user back to Fragment A
Here is an easy method to add fragments to fragments or to adapters within fragments...
from your base activity, make your fragment manager static. assume this activity is called dashboard.
static FragmentManager support;
Don't forget to initialize this in onCreate.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_dashboard);
support = getSupportFragmentManager();
define your new fragment inside your adapter or fragment.
users_item_fragment dialog = new users_item_fragment();
//also, let's add some data...
Bundle args = new Bundle();
args.putString("device", devicesList.get(position));
use the following method to add the fragment easily wherever you would like
//pick an easily remembered tag
public void replace(Fragment fragment, String tag){
FragmentManager man = dashboard.support;
FragmentTransaction fragt = man.beginTransaction();
if(!fragment.isAdded()) {
dashboard.lastTag = dashboard.fragtag;//not needed, but helpful w/ backpresses
fragt.add(R.id.fragment_container, fragment, tag)
.hide(man.findFragmentByTag(fragtag)).commit();
dashboard.fragtag = dashboard.tag;//not needed, but helpful w/ backpresses
}
if(fragment.isAdded() && fragment.isHidden()) {
dashboard.lastTag = dashboard.fragtag;//not needed, but helpful w/ backpresses
fragt.show(fragment);
fragt.hide(man.findFragmentByTag(fragtag)).commit();
dashboard.fragtag = dashboard.tag;//not needed, but helpful w/ backpresses
}
}
To implement this with backpresses working correctly, add this in you onBackPress method of your main activity:
#Override
public void onBackPressed() {
FragmentManager man = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = man.beginTransaction();
fragmentTransaction.hide(getSupportFragmentManager().findFragmentByTag(fragtag))
.show(getSupportFragmentManager().findFragmentByTag(lastTag)).commit();
fragtag = lastTag;// holds the last fragment
}
}
It's easy to see the logic here and easy to manipulate back press events using this.

Fragment not showing previous data when coming from another fragment

In my android activity I am using multiple fragments, I am successfully switching these fragments according to my requirement but problem is that when I am switching Fragment 1 to Fragment 2 and back from Fragment 2 to Fragment 1, Fragment 1 not showing previous data Fragment 1 start from stretch, but I want to show previous data same as I was selected.
Here is my code for go Fragment 1 (fragment_search_customerProfile) to Fragment 2 (fragment_CustomerInfo):
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.filterFram, new fragment_Search());
FrameLayout layout = (FrameLayout) rootView.findViewById(R.id.cpFrame);
layout.removeAllViewsInLayout();
transaction.remove(new fragment_search_customerProfile()).commit();
Here is my code for back Fragment 1 (fragment_search_customerProfile) fromFragment 2 (fragment_CustomerInfo):
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
Log.d("fragment_Search", fromSearch + "");
transaction.replace(R.id.custIndoFram, new fragment_search_customerProfile());
FrameLayout layout = (FrameLayout) rootView.findViewById(R.id.custIndoFram);
layout.removeAllViewsInLayout();
transaction.remove(new fragment_CustomerInfo()).commit();
Can anyone explain me how can I stay save my fragment data?
Instead of transaction.replace(R.id.filterFram, new fragment_Search());
you can use transaction.add to add fragment while having the data of first one
transaction.add(R.id.filterFram, new fragment_Search());
transaction.addToBackStack(null);
From fragment two you can use .show instead of replace to show the first fragment
and hide fragment two from the first one. transaction.show(getSupportFragmentManager().findFragmentByTag("firstFragmentTag")).commit();
You can simple keep the instances of the fragments, so in your Activity you would have fields like that.
private Fragment fragment1;
private Fragment fragment2;
And now you can use the sexy replace() option,
To add Fragment1
if (fragment1 == null) {
fragment1 = new fragment_Search();
}
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.filterFram, fragment1);
and same for Fragment2
And you can hold the state of the fragment (data it retrieves) in the Fragment itself, and in your onViewCreated() you check, if the state is not null, you update the view immediately.
class Fragment1 extends Fragment {
private State state;
public void onViewCreated(View v) {
if (state != null) {
// Update UI here
}
}
}
UPDATE
Using your own code
private Fragment fragment1; // This is a field outside of below method
if (fragment1 == null) {
fragment1 = new fragment_Search();
}
FragmentManager manager = getChildFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.filterFram, fragment1).commit();
FrameLayout layout = (FrameLayout) rootView.findViewById(R.id.cpFrame);
layout.removeAllViewsInLayout();
Going to fragment2 should be the same, and this code is used to go to Fragment1 and Fragment2, doesn't matter if you are going back or first time.
You have to add fragment instead of remove or replace

How to reuse existing fragment from FragmentManager instead of re-creating them again?

I am using NavigationDrawer in my application and each menu item in drawer is a fragment.Whenever user chooses a menu item I replace the current fragment in the main container with the requested one but it recreates the fragment every-time, so i updated my code to reuse the existing fragments instead of creating them again and again as content of fragments remain same. My updated code to show fragment is :
public void showTabFragment() {
TabFragment Tf = (TabFragment) mFragmentManager.findFragmentByTag(Constants.TAB_FRAGMENT);
mFragmentTransaction = mFragmentManager.beginTransaction();
if (Tf != null) {
mFragmentTransaction.replace(R.id.containerView, Tf, Constants.TAB_FRAGMENT);
} else {
mFragmentTransaction.replace(R.id.containerView, new TabFragment(), Constants.TAB_FRAGMENT);
}
mFragmentTransaction.commit();
}
In above code I am trying to get fragments by Tag but it always returns null and executes the else case(new fragment).Could someone please guide me what am I doing wrong in my code?
I guess the code you've shown is for one of your menu fragment? If that's the case, what is probably happening is every time you open a menu item, the container is replaced with the new fragment(say, Fragment B) with its new tag(say, TAG 'B'). So, when you try to open the previous fragment(say, Fragment A) using it's tag(TAG 'A'), it won't be there, because that's what you replaced.
One possible solution is to hold references to the fragment as they are created, in, say a hashmap, and reuse them instead.
private HashMap<String, Fragment> menuFragments = new HashMap<>();
public void showMenu(String fragmentID)
{
MenuFragment fragment = menuFragments.get(fragmentID);
if(fragment == null)
{
fragment = new MenuFragment(); //Create the respective menu fragment based on the ID.
menuFragments.put(fragmentID, fragment);
}
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.containerView, fragment, fragmentID);
transaction.commit();
}

Detecting when a fragment is on top again (has focus)

In my app, my activity has a container with a fragment. When a button in that fragment is clicked, I add a new fragment to the container and add the previous fragment to the backstack.
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.primary_fragment_container, homeFragmentHandler);
fragmentTransaction.addToBackStack(folderId.toString());
fragmentTransaction.commit();
The result is that pressing back closes the top fragment and returns me to the previous fragment, which is exactly how I want it. HOWEVER there are certain elements on the fragment that should be refreshed at this point. When the user clicks back and is returned to the previous fragment, how do I know to refresh the data within that fragment? is there a method such as onResume() for this scenario?
You have to proper add fragments to backstack:
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
MyFragment fragment = new MyFragment ();
transaction.replace(R.id.primary_fragment_container, fragment).addToBackStack(null).commit();
Fragment homeFragmentHandler= new Fragment();
Bundle bundle = new Bundle();
//replace this line below wth something convinient
bundle.putInt("key", value);
fragment.setArguments(bundle);
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.primary_fragment_container, homeFragmentHandler);
fragmentTransaction.addToBackStack(folderId.toString());
fragmentTransaction.commit();
This will send data regarding which fragment/ activity caused the previous fragment to load. Now in its onCreate add this code to check the data in the bundle
//In the onCreate of the original fragment
Bundle bundle = this.getArguments();
if(bundle.getInt(key, defaultValue)!=null{
int myInt = bundle.getInt(key, defaultValue);
// Load the data that needs to be refreshed when original fragment is loaded from the second one
}
No else statement is needed. If the fragment is made from any other activity/ fragment then the bundle.getInt will return null and hence that code inside the statement wont be executed.

Categories

Resources