reset addToBackStack() on MainActivity - android

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

Related

Why I need to press back button twice to dismiss fragment on first time?

I have Base Activity including NavigationView with 2 menu items. On start it loads Home fragment having background image inside it. Each loads specific fragment. When I select Terms & Conditions menu item, it loads T&C fragment & when I press back button it simply kills it.
However, when I select About Us menu item, it loads About Us fragment but I need to press BACK button twice to kill it. I need to know why does it happen?
Part of Code in AppBaseActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
fragmentManager = getSupportFragmentManager();
fragmentTransaction = fragmentManager.beginTransaction();
HomeFragment homeFragment = new HomeFragment();
fragmentTransaction.add(R.id.body_container, homeFragment, "");
fragmentTransaction.commit();
}
#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 :
fragmentTransaction = fragmentManager.beginTransaction();
TCFragment tcFragment = new TCFragment();
fragmentTransaction.replace(R.id.body_container, tcFragment, "");
fragmentTransaction.commit();
break;
case R.id.nav_about_us :
fragmentTransaction = fragmentManager.beginTransaction();
AboutUsFragment aboutUsFragment = new AboutUsFragment();
fragmentTransaction.replace(R.id.body_container, aboutUsFragment, "");
fragmentTransaction.commit();
break;
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
All fragments simply have overridden onCreateView() by inflating respected xml only. No code is written in both fragments yet.
You can stop back hardware navigation if you want.
Simply using onBackPressed() without super.onBackPressed()
#Override
public void onBackPressed() {
}
#Override
public void onBackPressed() {
super.onBackPressed();
}

How to remove all fragments except first one?

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"

prevent same fragment multiple times in backstack

Hi guys here is my code
navigationView.setNavigationItemSelectedListener(new NavigationView
.OnNavigationItemSelectedListener() {
#Override public boolean onNavigationItemSelected(#NonNull MenuItem item) {
if (item.isChecked()) {
//item already selected. Do nothing
drawerLayout.closeDrawer(GravityCompat.START);
return true;
}
switch (item.getItemId()) {
case R.id.home:
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.replace(R.id.fragment, new HomeFragment())
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE).commit();
break;
case R.id.other:
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment, new OtherFragment())
.addToBackStack(null).setTransition(FragmentTransaction
.TRANSIT_FRAGMENT_FADE).commit();
break;
default:
break;
}
drawerLayout.closeDrawer(GravityCompat.START);
return true;
}
});
I basically only have 2 fragments and merely the HomeFragment should be added to the backstack. After switching between HomeFragment and OtherFragment for a while and clicking on the back button while on the HomeFragment, I end up with the HomeFragment getting displayed several times.
Within the HomeFragment lies a recyclerview. When I scroll up and down I can really see that the rows are displayed multiple times.
How can I make sure that the HomeFragment is added to the backstack only once.
Thanks
You could easily check your fragment with
YourFragment.isAdded
And if you have a multiple fragment you could create a new class to manage all the fragment and create state to check if fragment have been added or not.
When you click on the Home menu item try to find out whether there is any fragment in the backstack or not. If yes, call popBackStack() or replace a fragment as you do now otherwise. Use this code: if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
}

Reselecting correct NavigationView menu item after pressing back button

I have an android application which has a NavigationView with 4 fragments. I can navigate between fragments via navigation menu and when I select another fragment, I added the previous fragment into back stack to provide back button functionality.
My problem is that when I press back button to go to previous fragment, the NavigationView still shows old fragment as the selected fragment. If it is possible I want to update selected option as the fragment on the screen.
Example:
I start from A, and select B from NavigationView. The current screen is B and NavigationView shows the selected item as B. If I press back button my current screen becomes A again but NavigationView shows B as selected item.
Here is my onNavigationItemSelected method:
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
HomeFragment fragment = null;
Class type = null;
switch (id) {
case R.id.nav_home:
type = HomeNavigationFragment.class;
break;
case R.id.nav_groups:
type = GroupsNavigationFragment.class;
break;
case R.id.nav_profile:
type = ProfileNavigationFragment.class;
break;
case R.id.nav_messages:
type = MessageNavigationFragment.class;
break;
}
fragment = HomeFragment.newInstance(mUser, type);
FragmentManager manager = getSupportFragmentManager();
manager.beginTransaction().replace(R.id.fragment_container, fragment).addToBackStack("fragment" + code++).commit();
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
Thanks.
I fixed my problem with overriding onBackPressed like this:
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
FragmentManager manager = getSupportFragmentManager();
if(manager.getBackStackEntryCount() > 0) {
super.onBackPressed();
HomeFragment currentFragment = (HomeFragment) manager.findFragmentById(R.id.fragment_container);
if(currentFragment instanceof HomeNavigationFragment){
mNavigationView.getMenu().getItem(0).setChecked(true);
}
else if(currentFragment instanceof GroupsNavigationFragment){
mNavigationView.getMenu().getItem(2).setChecked(true);
}
else if(currentFragment instanceof ProfileNavigationFragment){
mNavigationView.getMenu().getItem(1).setChecked(true);
}
else if(currentFragment instanceof MessageNavigationFragment){
mNavigationView.getMenu().getItem(3).setChecked(true);
}
}
}
}

NavigationView with Fragments - Toolbar and Navigation Item Sync On Back Key Pressed

I have implemented NavigationView in Android with fragments. This is my first time dealing with fragments. The problem is I have 3 Items in NavigationView and want to do something like that:
User enters home screen and "Top 20 Fragment" is selected by default (in this moment back key makes app exit) - I am setting this fragment as default without pulling transaction to back stack,
private void setStartingFragment(NavigationView navigationView,
FragmentManager fragmentManager) {
Fragment fragment = null;
Class fragmentClass = Top20RecipesFragment.class;
try {
fragment = (Fragment) fragmentClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frame_layout_content, fragment);
fragmentTransaction.commit();
MenuItem menuItem = navigationView.getMenu().getItem(0);
menuItem.setChecked(true); // Highlight The Selected Item
setTitle(menuItem.getTitle()); // Updating Toolbar Title
}
When I pick one of 2 other fragments I would like them to add to back stack. I want to do this like in Twitch mobile application. Possibilities are:
2nd Fragment (or 3rd) -> Home fragment -> Exit or
2nd Fragment -> 3rd Fragment -> Home Fragment -> Exit or
2nd Fragment -> 3rd Fragment -> Home Fragment -> Exit or
Another big problem is to sync NavigationView selected item and Tooblar title on back button pressed.
Here is what I have for now if we talk about NavigationView and fragments replacing:
// Replace Existing Fragment With a New One
public void selectDrawerItem(MenuItem menuItem) {
Fragment fragment = null;
Class fragmentClass = null;
switch(menuItem.getItemId()) {
case R.id.nav_top20_recipes: {
fragmentClass = Top20RecipesFragment.class;
break;
}
case R.id.nav_kitchen_type: {
fragmentClass = KitchenTypeFragment.class;
break;
}
case R.id.nav_meal_type: {
fragmentClass = MealTypeFragment.class;
break;
}
}
try {
fragment = (Fragment) fragmentClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frame_layout_content, fragment);
fragmentTransaction.addToBackStack(Integer.toString(fragment.getId()));
fragmentTransaction.commit();
menuItem.setChecked(true); // Highlight The Selected Item
setTitle(menuItem.getTitle()); // Updating Toolbar Title
mDrawerLayout.closeDrawers(); // Close The Drawer
}
I looked into Android official site and they advice to use OnBackStackChangedListener to change Activity apperance according to fragment replacing and using back button.
I tried to apply this interface and here what I tried to do with different toutorials but app crashes or does not work properly.
// Managing Fragments In Back Stack
#Override
public void onBackStackChanged() {
int actualStackHeight = getSupportFragmentManager().getBackStackEntryCount();
if (actualStackHeight > 0) {
FragmentManager fragmentManager = getSupportFragmentManager();
int fragmentId = fragmentManager.getBackStackEntryAt(actualStackHeight - 1).getId();
Fragment currentFragment = fragmentManager.findFragmentById(fragmentId);
switch (currentFragment.getId()) {
case R.id.top20_fragment: {
MenuItem menuItem = nvDrawer.getMenu().getItem(0);
menuItem.setChecked(true); // Highlight The Selected Item
setTitle(menuItem.getTitle()); // Updating Toolbar Title
}
case R.id.kitchen_type_fragment: {
MenuItem menuItem = nvDrawer.getMenu().getItem(1);
menuItem.setChecked(true); // Highlight The Selected Item
setTitle(menuItem.getTitle()); // Updating Toolbar Title
}
case R.id.meal_type_fragment: {
MenuItem menuItem = nvDrawer.getMenu().getItem(2);
menuItem.setChecked(true); // Highlight The Selected Item
setTitle(menuItem.getTitle()); // Updating Toolbar Title
}
}
} else {
finish();
}
}
This is my first time dealing with something like this so please try to understand me. I've searched for different ansewers even here on stack but non of them helped me :/
If you are using NavigationView use it with the Navigation API.
Then, on your onCreate(..) method...
main_navigationview.setupWithNavController(
findNavController(R.id.main_nav_container)
)
findNavController(
R.id.main_nav_container
).addOnDestinationChangedListener {
controller, destination, bundle ->
main_navigationview.setCheckedItem(destination.id)
}
Then, the NavigationView will be in sync, as simple as that.

Categories

Resources