I've looked around but found no real solution to my particular issue. So I have a navigation main activity, which consists of a navigation drawer and a frame layout for my content.
In my nav drawer are three buttons, each of which fill the frame layout with a specific fragment. One of those fragments acts as a "master view", and clicking on an item in that fragment then opens another fragment which is the "details view".
The problem I am having is this: If I am in the details view fragment, and I click on another button in my nav drawer to go to another fragment, then click on the nav drawer button to get back to my details view then it's fine, I get there ok. When I hit the back button however it takes me back to the previous fragment rather than the master view fragment. Is there any way I can get around this?
Here is a snippet of how I am moving between different fragments in my nav drawer:
private void selectItem(int position)
{
FragmentManager fragmentManager = getFragmentManager();
Fragment fragment = null;
Bundle args = new Bundle();
switch (position)
{
case 0:
fragment = new ReportIt();
fragment.setArguments(args);
break;
case 1:
Fragment f = fragmentManager.findFragmentByTag("article_view");
if (f == null)
{
fragment = new ReadIt();
fragment.setArguments(args);
}
else
{
fragment = f;
}
break;
case 2:
fragment = new FindIt();
fragment.setArguments(args);
break;
default:
fragment = new ReportIt();
fragment.setArguments(args);
break;
}
fragmentManager.beginTransaction()
.replace(R.id.content_frame, fragment).addToBackStack(null)
.commit();
mDrawerList.setItemChecked(position, true);
setTitle(mNavTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
}
And my master view fragment passing control to the details fragment:
FragmentManager fragmentManager = getFragmentManager();
Fragment fragment = new ReadItDetail();
Bundle args = new Bundle();
args.putString("ArticleId", pr.GetId());
fragment.setArguments(args);
fragmentManager.beginTransaction().addToBackStack(null)
.replace(R.id.content_frame, fragment, "article_view")
.commit();
So as you can see from the graph, All 3 of the navigation drawer buttons are part of my main activity. Also in my main activity is a framelayout which uses the fragments shown. All boxes outside of the navigation drawer button box are fragments. The master and detail are what I am having issues with. When I move around to other fragments, and then come back to my details fragment, hit the back button, I want it to go back to the master fragment 100% of the time rather than moving back to whatever other fragment I may have been on previously.
To control what Fragment you go back to you just need to control the entries in the backstack. FragmentManager will let let you pop the backstack so you can reset what back does once you navigate to the details fragment. There are a number of posts about this on SO - here's one and here's amonther that provides some more detail.
The one problem with this is that by overriding the natural flow of the back stack you run the risk of creating navigation that does not stem logically from the app. But you know your structure better than the system. If it makes sense to always go from detail to master, go for it.
Related
My application contains AppBaseActivity having NavigationView with few menu items. By default, I load Home fragment & on clicking each menu item from drawer, I open specific fragment.
My problem is, I need keep Home fragment all the time showing if user clicks back button.
Stepwise explanation :
On activity launch, loads Home fragment by default
Suppose selects Menu Item 1, loads related fragment[*4]
Suppose selects Menu Item 2, loads related fragment[*4]
I want to make back stack clear however, keeping Home fragment persistent so that if user presses back button instead of opting menu item from drawer or simply go to any fragment & on killing it, should navigate back to Home fragment.
In my current case, it simply closes/terminates my app.
AppBaseActivity Java (some part of code)
onCreate() {
fragmentTransaction = fragmentManager.beginTransaction();
HomeFragment homeFragment = new HomeFragment();
fragmentTransaction.add(R.id.body_container, homeFragment, getResources().getString(R.string.app_name));
fragmentTransaction.commit();
}
#SuppressWarnings("StatementWithEmptyBody")
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
navigationView.getMenu().findItem(item.getItemId()).setChecked(true);
switch (item.getItemId()) {
case R.id.nav_terms :
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
fragmentTransaction = fragmentManager.beginTransaction();
TCFragment tcFragment = new TCFragment();
fragmentTransaction.add(R.id.body_container, tcFragment, getResources().getString(R.string.tc_screen_name));
fragmentTransaction.commit();
break;
case R.id.nav_about_us :
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
fragmentTransaction = fragmentManager.beginTransaction();
AboutUsFragment aboutUsFragment = new AboutUsFragment();
fragmentTransaction.add(R.id.body_container, aboutUsFragment, getResources().getString(R.string.about_us_screen_name));
fragmentTransaction.commit();
break;
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
try by adding this line with home fragment before commit:
fragmentTransaction addTobackStack(null);
You need to add the transaction to the backstack using addToBackStack(). The back press would automatically pop the topmost fragment from the backstack.
Refer to
https://developer.android.com/guide/components/fragments.html
The relevant section is "Performing Fragment Transactions"
My MainActivity contains a Navigation Drawer with two fragments. The first fragment is loaded automatically when the app starts. I want the app to switch from fragment two to fragment one when the back button is pressed or if the first fragment is added then exit the app. I am adding the fragmentTransaction of the first fragment to a stack and then calling popBackStack in my onBackPressed method. However the behaviour is pretty weird.
When I'm on the first fragment the application should exit (ie, execute super.onBackPressed) however when on the first fragment and pressing back the first fragment is removed from the fragmentholder leading to a blank screen and then on pressing the second time the app closes.
When I'm on the second fragment nothing happens when the back button is pressed the first time and on pressing the back button a second time the app closes. Here's the relevant code from the MainActivity.java
#Override
public void onBackPressed() {
if (musicService.isPng()) moveTaskToBack(true);
if (getFragmentManager().getBackStackEntryCount() > 0)
getFragmentManager().popBackStack("returnFragment", 0);
else super.onBackPressed();
}
private void loadSelection(int i) {
navList.setItemChecked(i, true);
switch (i) {
case 0:
FirstFragment firstFragment = new FirstFragment();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragmentholder, firstFragment)
.addToBackStack("returnFragment")
.commit();
break;
case 1:
SecondFragment secondFragment = new SecondFragment();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragmentholder, secondFragment)
.commit();
break;
case 2:
musicService.removeNotification();
musicService.stopSelf();
MainActivity.this.finish();
}
}
loadSelection(0) is called in the onCreate of the MainActivity
Unlike given in the code, I have tried various modes of implementing the popBackStack() method but all of them lead to the same result. Just to add I don't want to implement a workaround since there are only 2 fragments since I am already working on adding new fragments.
try this,
call method .addToBackStack("returnFragment") while loading second fragment and remove it from first transaction.
In my activity, I have various fragments. By default activity displays a map. On listitem click, Fragment A, B or C is displayed using following code:
protected void replaceFragment(int i) {
FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
switch (i) {
case FRAGMENT_A:
aFragment = new AFragment ();
fragmentTransaction.replace(R.id.main_framelayout_replace,
aFragment , TAG_A_FRAGMENT);
fragmentTransaction.commit();
break;//and so on.....
default:
break;
}
}
Here I'm facing an issue: When I replace Fragment A with Fragment B which is nested fragment i.e. it contains list and detail fragment within itself. When I try to remove any fragment other than Fragment B, I'm able to do it successfully and show the default map screen but when I'm on Frgament B and try to remove it, I'm not able to see default map screen. Instead a blank white screen is getting displayed.
Removing of fragments is done as follows:
if (aFragment != null) {
fragmentManager.beginTransaction()
.remove(aFragment ).commit();
}//and so on...
For fragment B which is having list and detailed fragment, I also do following in onDetach of fragment B,
fragmentManager.beginTransaction()
.remove(MainActivity.listFragment).commit();
Note: I'm not getting exception in any try catch. All the lines of code are getting executed without error including onDetach of Fragment B.
Am I doing anything wrong here? Any help appreciated.
I am using navigation drawer and it is simple to use. I am not providing the complete code but providing you detail which could be easy for you to understand my problem. I am using fragments these are about 8 in numbers and I am replacing them with one an other. But here comes a problem
I am replacing them on click event of the navigation drawer. but there are two main problems
After replacement , I can see the previous fragment in the background. does replace method just call the new fragment over it ? if yes then what should I do to old fragment not be visible in the background of my new fragment.
When I click navigation drawer Item , it loads the specific fragment successfully. but keeping in that fragment when I click to that specific item again it loads this fragment again and again. For example if drawer item num 3 opens fragment MyBook , then by clicking item num three 2 or many times would open fragment that much time.
So please some one answer me how to cure my app for such kind of actions which I described above.
I tried like this. Its working fine me
FragmentManager frgmanager = getFragmentManager();
frgmanager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
FragmentTransaction frgTransaction = frgmanager.beginTransaction();
if(subitem.equalsIgnoreCase("subitem")){
Frag1 frg1 =new Frag1(mCtx);
frgTransaction.replace(R.id.inflate_layout, frg1);
}else if(subitem1.equalsIgnoreCase("subitem1")){
Frag2 frg2 =new Frag2(mCtx);
frgTransaction.replace(R.id.inflate_layout, frg2);
}else{
Frag2 frg3 =new Frag3(mCtx);
frgTransaction.replace(R.id.inflate_layout, frg3);
}
frgTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
frgTransaction.commit();
you can use addtobackstack in fragmentstranstion object.like
FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.bodyfragment, new AnotherFragment());
transaction.addtoBackStack(null).commit();
Use replace-method of FragmentTransaction instead of add (http://developer.android.com/guide/components/fragments.html#Transactions)
FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.bodyfragment, new AnotherFragment());
transaction.commit();
To avoid re-instantiating the fragment, keep track of the current open fragment and only do a fragment transaction, if we next-to-be-opened fragment is a different one than the current.
This may achieved like the following:
class MyActivity ... {
private String currentFragment;
private void openNewFragment(Fragment fragment) {
String newFragment = fragment.getClass().getSimpleName();
if (newFragment.equals(currentFragment)){
// new fragment already shown
return;
}
// Fragment transaction etc here:
}
}
Note that this only compares fragments based in their class name. Sometimes this might not be unique, e.g. if there is a DetailFragment class which displays information about an entity. Which entities details to show may depend on intent arguments.
The above code however will then prevent opening DetailFragment for Entity=1 if currently details for Entity=2 are shown. For these scenarios the information about the fragment kept needs to be extended (e.g. storing a Reference or WeakReference to the fragment instance itself).
I have an activity with a container for fragments and a NavigationDrawer. If I select an item in the drawer, I call updatePage(index).
I also call updatePage(0) if I create the activity and the savedInstanceState == null to init my activity.
One fragment has a sub fragment and therefor it just replaces itself by the subfragment and adds the subfragment to the backstack, so that the user can navigate back to the previous fragment. In this fragment I call following code directly:
#Override
public void onClick(View view)
{
Event event = (Event)view.getTag();
FragmentManager fm = getActivity().getSupportFragmentManager();
GamesFragment f = new GamesFragment();
Bundle bundle = new Bundle();
bundle.putParcelable(GamesFragment.KEY_EVENT, event);
f.setArguments(bundle);
FragmentTransaction ft = fm.beginTransaction()
.replace(R.id.frame_container, f, f.getClass().getName())
.addToBackStack(null);
ft.commit();
}
Why does sometimes the removing of the old fragment not work? I get overlaying fragments, but only sometimes.
My activities updatePage function looks like following:
private void updatePage(int drawerSelection)
{
mDrawer.closeDrawer();
Fragment f = null;
FragmentManager fm = getSupportFragmentManager();
switch (drawerSelection)
{
case 0:
f = fm.findFragmentByTag(HomeFragment.class.getName());
if (f == null)
f = new HomeFragment();
break;
case 1:
f = fm.findFragmentByTag(EventFragment.class.getName());
if (f == null)
f = new EventFragment();
break;
default:
break;
}
if (f != null && !f.isAdded())
{
// SOLUTION:
// Backstack clearen
// fm.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.frame_container, f, f.getClass().getName());
ft.commit();
}
}
I saw such situation few times and I bet that it can happen after replacing with addToBackStack. Easiest fix is to add background to fragment layout. From android doc:
Note: When you remove or replace a fragment and add the transaction to the back stack, the fragment that is removed is stopped (not destroyed). If the user navigates back to restore the fragment, it restarts. If you do not add the transaction to the back stack, then the fragment is destroyed when removed or replaced.
This view is not redrawed entirely just overdrawed and if second fragment doesn't has background or view container redraw you will get effect as described. That's my theory :>. Sometimes Android documentation is not as clear as we would like it to be.
The problem is following use case:
Going to the EventFragment from the GameFragment pushes the "remove(EventFragment).add(GameFragment)" transaction to the backstack. If I now press the back button, this transaction will be undone and everything is fine. The backstack is empty again and everything works. BUT, if I don't press the back button, but change to another fragment through the menu, the backstack does still have the above mentioned transaction. Pressing back will now try to undo this transaction... It will just readd the EventFragment before the code in my menu click handler adds the fragment from the menu... This is how it could happen...
Easy solution, if I only want one backstack for every menu entry and want to delete the backstack, if I select another area of my app in the menu is, to clear the backstack before I go to another area through the menu...
So adding ' fm.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);' before replacing the the current fragment will solve the problem (I added this solution as a comment to my main post)