I have Fragment A that lets the user access Fragment B by the click of a button, when an action is performed on B, user is redirected to Fragment C to retrieve a last bit of information before returning to A.
(A -> B -> C -> A).
After doing this, if I open my Navigation Drawer and click on the Fragment I am currently on (A), its contents are gone, only the Drawer itself remains.
I can't seem to explain the reason behind this.
Here is how I conserve my Navigation Drawer's Fragment states:
// User clicks on an item in Nav. Drawer, call this method
private Fragment checkFragmentState(int itemId) {
Fragment fragment = null;
switch (itemId) {
// HOME
case R.id.nav_home:
if (home == null) {
fragment = new Home();
home = fragment;
} else
fragment = home;
break;
// SEARCH CARD
case R.id.nav_searchCard:
if (searchCard == null) {
fragment = new SearchCard();
searchCard = fragment;
} else
fragment = searchCard;
break;
}
return fragment;
}
I call this method when my user clicks on an element in the Drawer, basically it checks if the Fragment has already been created, if so, it saves the currently existing one and uses it to be displayed.
What could cause this odd behavior?
Just launch a new instance each time
// User clicks on an item in Nav. Drawer, call this method
private Fragment checkFragmentState(int itemId) {
Fragment fragment = null;
switch (itemId) {
case R.id.nav_home:
fragment = new Home();
break;
case R.id.nav_searchCard:
fragment = new SearchCard();
break;
default:
fragment = new Fragment();
}
return fragment;
}
Related
I am using MeowBottomNavigation and from one fragment, I need to redirect to another fragment. but when I am doing that, the navbar displays the old menu selection.
Please help me.
This is on Home Fragment
enter image description here
Now, its displaying data from 2nd fragment but nav shows HOME only. Ti should be 2nd nav.
enter image description here
CODE
getSupportFragmentManager().beginTransaction().replace(R.id.framelayout, fragment).commit();
bottomNavigation = findViewById(R.id.bottomNav);
bottomNavigation.add(new MeowBottomNavigation.Model(1, R.drawable.ic_nav_home));
bottomNavigation.add(new MeowBottomNavigation.Model(2, R.drawable.ic_baseline_category_24));
bottomNavigation.setOnShowListener(new MeowBottomNavigation.ShowListener(){
#Override public void onShowItem(MeowBottomNavigation.Model item) {
Fragment fragment = null;
switch (item.getId()){
case 1: fragment = new HomeFragment(); break;
case 2: fragment = new CategoryFragment(); break;
}
fragmentLoad(fragment);
}
});
bottomNavigation.show(1, true);
}
public void fragmentLoad(Fragment fragment) {}
I am building an android application using default Navigation Drawer layout and Fragment to switch from one layout to another.
The layout sequence is like
Dashboard -> Settings -> Profile Settings
MailActivity.class loads main_activity.xml which containes a frameLayout to contain fragment
and Dashboard loads ActivityDashboard Fragment.
Now, when I switch from Dashboard to Settings and then Profile Settings. onBackPress works fine. But after on Profile Settings, I selected Dashboard from Navigation Drawer to go back to the home screen. Then pressing back key follows the stack
Dashboard -> Profile Settings -> Settings -> Dashboard
The code for Navigation Drawer is
#SuppressWarnings("StatementWithEmptyBody")
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// calling the method displaySelectedScreen()
displaySelectedScreen(item.getItemId());
return true;
}
private void displaySelectedScreen(int itemId) {
// creating Fragment object
Fragment fragment = null;
// initializing fragment
switch (itemId) {
case R.id.nav_dashboard:
fragment = new ActivityDashboard();
break;
case R.id.nav_calendar:
fragment = new ActivityCalendar();
break;
case R.id.nav_history:
fragment = new ActivityHistory();
break;
case R.id.nav_about:
fragment = new ActivityAbout();
break;
case R.id.nav_settings:
fragment = new ActivitySettings();
break;
}
// replacing the fragment
if (fragment != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.addToBackStack(null);
ft.commit();
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
}
I'm using same FragmentTransaction code to switch from one fragment to another in ActivityDashboard, ActivitySettings fragment classes.
Q 1. How to flush the addBackToStack when the ActivityDashboard is loaded so that user have only option to exit app ?
Q 2. How to get back to ActivityDashboard, when layout is changed from Navigation Drawer irespective of which fragment is replaced ?
i.e., When user is on ActivitySettings fragment and changed to ActivityHistory from Navigation Drawer, then on backPress, user must be tracked following
ActivityHistory -> ActivityDashboard
instead of
ActivityHistory -> ActivitySettings -> ActivityDashboard
onBackPressed() method on MainActivity.class is
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
Simply call
fragmentManager.popBackStack(stackName /* null if you did not supply a stack name while creating your fragments */, FragmentManager.POP_BACK_STACK_INCLUSIVE);
inside your ActivityDashboard
I have a navigation drawer setup which on menu item click will replace the fragment container with whichever new fragment is being chosen in the menu:
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
switch (id) {
case R.id.nav_new:
if (subInfo.isEmpty()){
addFragment(new NewFragment(), id);
} else {
addFragment(new SubjectInfoFragment(), id);
}
break;
case R.id.nav_start:
addFragment(new StartFragment(), id);
break;
case R.id.nav_save:
addFragment(new SaveFragment(), id);
break;
case R.id.nav_raw:
addFragment(new RawFragment(), id);
break;
case R.id.nav_accelerometer:
addFragment(new AccelerometerFragment(), id);
break;
case R.id.nav_accelerometerWorld:
addFragment(new AccelerometerWorldFragment(), id);
break;
case R.id.nav_gyroscope:
addFragment(new GyroscopeFragment(), id);
break;
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
And the method for adding fragments:
public void addFragment(Fragment fragment, int id) {
if (fragment != null) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
if (fragmentManager.getFragments() == null) {
fragmentTransaction.replace(R.id.fragment_container, fragment, fragment.getClass().getSimpleName())
.commit();
} else {
fragmentTransaction.replace(R.id.fragment_container, fragment, fragment.getClass().getSimpleName())
.addToBackStack(Integer.toString(id))
.commit();
}
}
}
In one of my fragments (NewFragment()) there is a data insert form, the id of that menu item is nav_new. When the submit button is clicked, the addFragment method is called to direct the user to another fragment (StartFragment()) in my app:
mainActivity.addFragment(new StartFragment(), R.id.nav_new);
However, the above will add the data insert form to the backstack. Once the user has submitted the form, I don't want them to be able to press back to get back to the submission form. Instead I want to direct them to another fragment (SubjectInfoFragment()) which contains a summary of the information they've entered
I've designed my onNavigationItemSelected method to support this. When the nav_new menu item is clicked, it checks to see whether the user has already entered any info. If they haven't, then a transaction takes place that takes them to NewFragment to enter form info. If they have, SubjectInfoFragment is shown instead to display a summary of their previously entered info
I have to direct them to StartFragment on form submit, but then the old form fragment is added to the backstack, which is undesireable
After form submission, how do I handle fragment transactions such that the back button will take them to a completely new fragment (SubjectInfoFragment) instead of taking them back to the form submission page?
Or in the worst case scenario, if I cant add an entirely new fragment to the backstack, how can I at least prevent users from pressing back to the form submit page?
you should use the following line to add fragment to backstack:
getFragmentManager.addToBackStack(tag or null);
okay i know there are other questions that on first glance make this one look like a duplicate, but none of these answers work in my case,
What i want is the first fragment displayed to be like a Main Activity in respect to how the back button works, i need whichever fragment i choose from my navigation drawer to go back to the first fragment when the back button is pressed then a user would quit the app by pressing it again.
So ive tried using addToBackStack and when i move to another fragment if i press the back button it comes back to my first fragment (exactly as i want) but pressing the back button again leaves me with a white screen (i wonder if this is due to the transaction animation im using which ive included below) so to get around this i tried overriding the back button and throwing in a call to finish(); but this causes whichever fragment im in to finish instead of going back to the first fragment, ive tried a handful of workarounds from the above mentioned link and many others but cannot find a decent fix any suggestions?
here is my Main Activity displayView
private void displayView(int position) {
// update the main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
fragment = new FirstFragment();
break;
case 1:
fragment = new glideFrag();
break;
case 2:
fragment = new secondGlideFrag();
break;
case 3:
fragment = new thirdGlideFrag();
break;
case 4:
fragment = new forthGlideFrag();
break;
case 5:
fragment = new lgFrag();
break;
case 6:
fragment = new cyanFrag();
break;
case 7:
fragment = new sonyFrag();
break;
case 8:
fragment = new SecondFragment();
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.enter,R.anim.exit,R.anim.pop_enter,R.anim.pop_exit);
//fragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.frame_container, fragment).addToBackStack("first Fragment").commit();
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
mDrawerList.setSelection(position);
setTitle(navMenuTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
} else {
// error in creating fragment
Log.e("MainActivity", "Error in creating fragment");
}
i found this that looks like a great way around it
private boolean popNext = false;
if(popNext){
if(position == INITIAL_POSITION){
onBackPressed();
mDrawerLayout.closeDrawer(mDrawerList);
popNext = false;
return;
}
getSupportFragmentManager().popBackStackImmediate();
}
else{
if(position == INITIAL_POSITION){
mDrawerLayout.closeDrawer(mDrawerList);
return;
}
popNext=true;
}
but im still fairly new to android and im not sure what to set INITIAL_POSITION to, I tried
private static final INITIAL_POSITION = 0;
but without any luck
When adding the initial fragment, you must not add it to the back stack.
You must only do it for the next ones. When the back stack will be empty, the Activity will just finish.
Edit: Here is an explanation of the problem so you can figure out how to fix it:
Each time you add a fragment transaction to the back stack, you allow the user to revert it by pressing the back button and the Activity will return to the state it was before the transaction. If the initial fragment is added to the back stack, then when the user press back, the screen becomes blank, because there was nothing displayed before you added the initial fragment. That's why the initial fragment transaction which adds the first visible fragment to your Activity must not be added to the back stack. Usually you initialize the initial fragment like this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState == null) {
Fragment fragment = new FirstFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.frame_container, fragment)
.commit();
}
}
BladeCoders answer was more trying to tell me how the backstack works rather than answering my question, i ended up not adding any fragments to the back stack, .addToBackStack(null), and overriding back button in MainActivity, feels like a little bit of a hack but works perfectly
#Override
public void onBackPressed() {
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager.getBackStackEntryCount() < 1){
Fragment fragment = new FirstFragment();
FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.enter, R.anim.exit,
R.anim.pop_enter, R.anim.pop_exit);
getSupportActionBar().setTitle(mDrawerTitle);
fragmentTransaction.replace(R.id.frame_container,
fragment).addToBackStack("first").commit();
}else{
finish();
}
}
You can do it even with out backstack its just my point of view to simplify so that it can help some one.
#Override
public void onBackPressed(){
Fragment f = getSupportFragmentManager().findFragmentById(R.id.container_body);
if(f.getClass().getName().equals(HomeFragment.class.getName())){ // here HomeFragment.class.getName() means from which faragment you actually want to exit
finish();
}else{
displayView(0); //were you want to go when back button is pressed
}
}
private void displayView(int position) {
Fragment fragment = null;
String title = getString(R.string.app_name);
switch (position) {
case 0:
fragment = new HomeFragment();
title = getString(R.string.app_name);
break;
case 1:
fragment = new OffersFragment();
title = getString(R.string.nav_item_offers);
break;
case 2:
fragment = new NotificationFragment();
title = getString(R.string.nav_item_notifications);
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.container_body, fragment);
fragmentTransaction.commit();
// set the toolbar title
getSupportActionBar().setTitle(title);
}
}
I'm new to Android, and looks like there are still some things missing in my understanding of the activity lifecycle. I have a tabbed application created with a FragmentActivity. A custom ViewPager is managing the presentation of tabs and in one of those tabs I want a simple drill-down with the ability to use the hardware "Back" button to go back up a level to the previous view.
I implemented this drill-down by just substituting an appropriate Fragment into the tab by checking the value of a flag (which determines where in the drill down we are):
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
#Override
public Fragment getItem(int position) {
Fragment fragment;
switch (position) {
case 0 :
if (stateFlag == 0) fragment = (Fragment) new InitialFragment();
else fragment = (Fragment) new FinalResultsFragment();
break;
case 1:
fragment = (Fragment) new SecondTabFragment();
break;
default:
fragment = (Fragment) new ThirdTabFragment();
break;
}
return fragment;
}
To go back to the "previous" Fragment I do this:
#Override
public void onBackPressed() {
int p = mViewPager.getCurrentItem();
switch (p) {
case 0:
if (stateFlag == 0)) {
super.onBackPressed();
} else {
stateFlag = 0;
mViewPager.getAdapter().notifyDataSetChanged();
mViewPager.setCurrentItem(0);
}
break;
default:
super.onBackPressed();
break;
}
}
This works fine, until I try changing the orientation. The Fragments come back from that change successfully, but when I try pressing the Back button while in the FinalResultsFragment state, I get
IllegalStateException: Fragment already active
E/AndroidRuntime(1094): at android.support.v4.view.PagerAdapter.notifyDataSetChanged(PagerAdapter.java:276)
Why is the change in orientation breaking this particular part of the code?
Edit:
So if I open the app, first tab active, I press a button that loads a different fragment in that tab, then press the Back button - it's all good and the first fragment is loaded, as expected.
But if I open the app, first tab active, I press a button that loads a different fragment in that tab, the rotate the screen, the fragment is re-loaded just fine, then press the Back button - crash!
Edit:
here is the complete error log: http://pastebin.com/ESDTstsm