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.
Related
Good Day,
I am facing some problem with fragments
I am displaying a fragment when user clicks on 'More', as a popup menu
but when I click the 'More' again, it would be like add one more fragment on the previous one
can someone tell me how to remove the fragment when I click on the 'More' again? Thank you!
the java code of bottom navigation menu
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomNavigationView bottomNavigationView = (BottomNavigationView)
findViewById(R.id.bottom_navigation);
BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
String title = "";
switch (item.getItemId()) {
case R.id.menu:
getFragmentManager().beginTransaction().replace(R.id.fragment_container, new MenuFragment()).addToBackStack(null).commit();
title = "MENU";
getSupportActionBar().setTitle(title);
break;
case R.id.promotion:
getFragmentManager().beginTransaction().replace(R.id.fragment_container, new PromotionFragment()).addToBackStack(null).commit();
title = "PROMOTION";
getSupportActionBar().setTitle(title);
break;
case R.id.order:
getFragmentManager().beginTransaction().replace(R.id.fragment_container, new OrderFragment()).addToBackStack(null).commit();
title = "ORDER";
getSupportActionBar().setTitle(title);
break;
case R.id.location:
getFragmentManager().beginTransaction().replace(R.id.fragment_container, new LocationFragment()).addToBackStack(null).commit();
title = "LOCATION";
getSupportActionBar().setTitle(title);
break;
case R.id.more:
getFragmentManager().beginTransaction().add(R.id.fragment_container, new MoreFragment()).addToBackStack(null).commit();
break;
}
return true;
}});
}
}
Do not add your Popup Fragment to the backstack while transitioning to a new one. That is the first step you should do.
Next, rather than reopening the Popup on second "more" click, you should close it. Sounds like a better approach. But this would be probably better achieved by using Dialogs.
before calling transaction for 'more' add a tag with addToBackStack function(ex: moreFragment). check fragment manager and remove fragment like:
Fragment mFragment =getSupportFragmentManager().findFragmentByTag("moreFragment");
if(mFragment != null)
getSupportFragmentManager().beginTransaction().remove(mFragment);
But I think instead of removing old fragment just keep it and dont open new one if not null
To do this you have to keep track of fragment state. Lets assume a variable isFragmentOpen. In onClick you have to flip the variable and take your decision.
private boolean isFragmentOpen;
void onClick(View view) {
if (!isFragmentOpen) {
addFragment();
} else {
removeFragment();
}
// Flip the fragment state.
isFragmentOpen = !isFragmentOpen;
}
Please check how to add or delete fragment from official document Fragments
thanks for helping and now i already solve the problem^^
this is the code what i using
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
MoreFragment f = (MoreFragment) fm.findFragmentByTag("tag");
if(f == null) { // not added
f = new MoreFragment();
ft.add(R.id.fragment_container, f, "tag");
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
} else { // already added
ft.remove(f);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);
}
ft.commit();
break;
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();
}
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 an app which utilized NavigationDrawer and toolbar. When I click on one of the menu in the NavigationDrawer, it displays a fragment (let's call it fragment1). Inside fragment1 is a button, when clicked would display another fragment (fragment2).
Ok so when I'm at fragment1, the toolbar would display a hamburger icon, where when I click on it, the drawer would be opened. And when I'm at fragment2, the toolbar would display a back icon (up caret) instead of the hamburger. Ok this is correct, but when I click on the back icon (up caret) the drawer would be opened instead. So what should I do so that when the back icon (up caret) is clicked, I'd be brought back to fragment1, and not opening the drawer?
This is my onClick for the drawer
#Override
public void onClick(View v) {
int position = getPosition();
Fragment fragment = null;
FragmentManager fManager = ((FragmentActivity) contxt).getSupportFragmentManager();
FragmentTransaction transaction = fManager.beginTransaction();
Toast.makeText(contxt, "The Item Clicked isss: " + position, Toast.LENGTH_SHORT).show();
mDrawerLayout = (DrawerLayout) ((FragmentActivity) contxt).findViewById(R.id.dlMainDrawer);
mDrawerLayout.closeDrawers();
switch (position) {
case 1:
fragment = new MenuHome();
transaction.replace(R.id.container, fragment, "fragmentHome");
transaction.addToBackStack(null);
transaction.commit();
break;
case 2:
fragment = new MenuExpensesDaily();
transaction.replace(R.id.container, fragment, "fragmentExpensesDaily");
transaction.addToBackStack(null);
transaction.commit();
break;
default:
break;
}
}
So using the code above, I opened an instance of MenuExpensesDaily. And inside MenuExpensesDaily, when I click on a menu item, it would open another fragment, ExpensesDailyAdd, as shown below:
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu_main, menu);
MenuItem mItem = menu.findItem(R.id.tbAddExpenses);
mItem.setVisible(true);
mItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
ExpensesDailyAdd addExpenses = new ExpensesDailyAdd();
getFragmentManager().beginTransaction()
.replace(R.id.container, addExpenses, null)
.addToBackStack(null)
.commit();
return true;
}
});
}
Ok so now when ExpensesDailyAdd is opened, the hamburger icon is changed to the up caret. But when I click the up caret, it opens the drawer instead of going back to the previous fragment.
I have my navigation drawer set up just like the one in the android developers training.
Inside the navigation drawer i have two fragments right now. One of the fragments contains a listview, whose content is populated through an AsyncTask, which loads the items through a rather time consuming file system search. I already achieved to retain my list items through a configuration change:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState == null) {
new LoadPdfList().execute(Environment.getExternalStorageDirectory());
} else {
pdfThumbs = savedInstanceState.getParcelableArrayList("pdfThumbs");
}
}
But there is still one big problem, which i can't get solved. As soon as i switch my current Fragment with the navigation drawer and switch back to my previous fragment, which hosts the listview, the asynctask is reexecuted.
Although this behavior is not really surprising to me considering this part of the displayView-Code from the android developer tutorial:
private void displayView(int position) {
// update the main content by replacing fragments
Fragment newFragment = null;
switch (position) {
case 0:
newFragment = new DocumentsFragment();
break;
case 1:
newFragment = new PlayerFragment();
break;
default:
break;
}
if (newFragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.frame_container, newFragment).commit();
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
mDrawerList.setSelection(position);
setTitle(navMenuTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
currentFragmentId = position;
} else {
// error in creating fragment
Log.e("MainActivity", "Error in creating fragment");
}
}
I tried messing around with the different possibilities from the transactionmanager but still couldn't get it running.
I just want my fragments being retained while changing the my fragments through the navigation drawer.