I'm trying to develop an application using the Navigation drawer template of Android Studio. So, I created a new project using this template. But when I run the program and click on a menu item, the view doesn't change. I searched everywhere on internet, but I didn't see how I can handle this.
This is the code provided by Android Studio:
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_camara) {
} else if (id == R.id.nav_gallery) {
} else if (id == R.id.nav_slideshow) {
} else if (id == R.id.nav_manage) {
} else if (id == R.id.nav_share) {
} else if (id == R.id.nav_send) {
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
What I want to achieve is to replace the current view with the appropriate view for the menu item clicked.
Is Fragment the best way to do this or should I create a different activity for each menu item?
In your case adding fragment will be the best solution.
create a fragment BlankFragment.java
public class BlankFragment extends Fragment {
public BlankFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_blank, container, false);
}
}
and create fragment_black.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.above_inc.shyam.drawer.BlankFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="#string/hello_blank_fragment" />
</FrameLayout>
now replace your method
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
Fragment fragment = null;
if (id == R.id.nav_camera) {
// Handle the camera action
fragment = new BlankFragment();
} else if (id == R.id.nav_gallery) {
} else if (id == R.id.nav_slideshow) {
} else if (id == R.id.nav_manage) {
} else if (id == R.id.nav_share) {
} else if (id == R.id.nav_send) {
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.frame_container, fragment).commit();
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
add below code in your content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="com.above_inc.shyam.drawer.MainActivity"
tools:showIn="#layout/app_bar_main">
<FrameLayout
android:id="#+id/frame_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
you can also add more fragment to other options same as camera in above code
Fragment will be the best way if you want your navigation drawer to be shown on the next screen when you click on the menu item.
If you will use activity then navigation drawer will not be shown unless you make a BaseActivity which extends Navigation Drawer and use everywhere. In this case you have to change acitivty transition animation too as new activity will pop up after clicking on the menu which may look odd.
Right click on your project -> New -> Fragment.
Edit the switch case accordingly.
#Passiondroid's answer on using Fragments only if you want Navigation drawer across all activities is spot on.
About starting an activity on click of the menu item, in one of my apps, I follow this,
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
switch(id)
{
case R.id.nav_profile:
selection = 1;
intent = new Intent("android.intent.action.WEB");
intent.putExtra("selection",selection);
startActivity(intent);
break;
case R.id.nav_books:
intent = new Intent("android.intent.action.WEBD");
selection = 2;
intent.putExtra("selection",selection);
startActivity(intent);
break;
case R.id.fav_quotes:
intent = new Intent ("android.intent.action.SHOWFAVTES");
selection = 3;
intent.putExtra("selection",selection);
startActivity(intent);
break;
default:
break;
}
To change and configure the Menu Item, you can find the "activity_main_drawer.xml" under res->Menu.
And on click of each of the menu items, you can start an activity as per your wish. You have declare the activities in your AndroidManifest.xml file before you use it.
Beginning from android studio > 3.5 you will realize when you create navigation drawer activity it generates fragments opens when drawer items are clicked.
you can a listener if you want to open an activity.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow,
R.id.nav_tools, R.id.nav_share, R.id.nav_send,R.id.nav_accounts)
.setDrawerLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
#Override
public void onDestinationChanged(#NonNull NavController controller, #NonNull NavDestination destination, #Nullable Bundle arguments) {
int menuId = destination.getId();
switch (menuId){
case R.id.nav_gallery:
Toast.makeText(MainActivity.this,"You tapped gallery",Toast.LENGTH_LONG).show();
fab.hide();
break;
default:
fab.show();
break;
}
}
});
for full tutorial. Navigation Drawer
Related
Hey guys new to all this and can't seem to figure a way to change the Navigation Title with my navigation drawer selection. Made a stock standard navigation drawer with android studio.
I feel it's really simple but hope someone will give me an answer.
i watch a youtube 2014 by Derek Banas and all he added in his fragment class was
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
((MainActivity) activity).onSectionAttached(2);
}
but that doesnt seem to work anymore. is there something similair for Studio 3.0.1?
If you go to Android Studio, create New Project with the Navigation Drawer template, in MainActivity, you'll find the method onNavigationItemSelected. In that method, you can easily change activity title depending on your navigation option selection -
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_camera) {
// Handle the camera action
setTitle("Camera");
} else if (id == R.id.nav_gallery) {
setTitle("Gallery");
} else if (id == R.id.nav_slideshow) {
setTitle("Slideshow");
} else if (id == R.id.nav_manage) {
setTitle("Manage");
} else if (id == R.id.nav_share) {
setTitle("Share");
} else if (id == R.id.nav_send) {
setTitle("Send");
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
I have extended my newActivity to mainActivity fine and I got the navigation drawer working, but now it sort of forms a loop as it again picks up the click of newActivity in navigation drawer and opens up a new intent again,so basically I am opening intent inside intent inside intent, I want to move between items in drawer and not open a new intent for every click, what am I doing wrong?
My navigation Drawer(below) Listening for click event and opens a activity called main acitvity
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_camera) {
startActivity(new Intent(Main2Activity.this, MainActivity.class));
} else if (id == R.id.nav_gallery) {
} else if (id == R.id.nav_slideshow) {
} else if (id == R.id.nav_manage) {
} else if (id == R.id.nav_share) {
} else if (id == R.id.nav_send) {
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
Now as this drawer will also come with main activity as I have extended it from the activity with drawer, I get the option of clicking on the mainactivity option in new intent again, so this sort of gives me ability to open mainActivity any number of times, i dont want this
You should use fragments. Activity holds your main view and your activity drawer seperately. When you click some item in drawer, just replace your fragment on main container.
Check this : https://developer.android.com/training/implementing-navigation/nav-drawer.html#ListItemClicks
Edit
public void replaceFragment(Fragment fragment) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.mainContainer, fragment);
ft.commit();
}
put this method into your activity. And create a FrameLayout in your Main Layout.
<android.support.v4.widget.DrawerLayout...
<FrameLayout
android:id="#+id/mainContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Now you can set a fragment in onCreate() for the first screen using replaceFragment(Blala.newInstance()). When you click and item on Drawer just call replaceFragment(...) again.
I am using a single activity and multiple fragments(screenshot attached) within the same activity to provide a seamless navigation. But after implementing the latest toolbar and navigation view, it seems hard to handle the navigation and home buttons. I am having trouble with the following things.
Managing the Hamburger/Back button at left top. Toggling the icon and functionality to Menu and Back nav.
Page title - Changing the page titles whenever a fragment in pushed and popped.
I have tried several things like overriding onBackPressed(), setHomeAsUpIndicator, popping fragments manually. Earlier i was using ActionBarDrawer toggle to handle this but it is failing somehow now. I checked the google samples they seem to use separate activities at most of the places.
Can anyone guide me how to implement a proper back navigation to handle the NavigationView, Back button in inner fragments and page titles? I am using AppCompatActivity, android.app.Fragment, NavigationView and Toolbar.
It's much easier to illustrate with some sort of division of responsibility for your Activity and Fragment.
Problem 1: Managing the Hamburger/Back button at left top. Toggling the icon and functionality to Menu and Back nav.
From the illustration, the solution should be encapsulated by the Activity, which will look something like this:
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
private ActionBarDrawerToggle mDrawerToggle;
private DrawerLayout mDrawer;
private ActionBar mActionBar;
private boolean mToolBarNavigationListenerIsRegistered = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mActionBar = getSupportActionBar();
mDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
mDrawer.addDrawerListener(mDrawerToggle);
mDrawerToggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
// On orientation change savedInstanceState will not be null.
// Use this to show hamburger or up icon based on fragment back stack.
if(savedInstanceState != null){
resolveUpButtonWithFragmentStack();
} else {
// You probably want to add your ListFragment here.
}
}
#Override
public void onBackPressed() {
if (mDrawer.isDrawerOpen(GravityCompat.START)) {
mDrawer.closeDrawer(GravityCompat.START);
} else {
int backStackCount = getSupportFragmentManager().getBackStackEntryCount();
if (backStackCount >= 1) {
getSupportFragmentManager().popBackStack();
// Change to hamburger icon if at bottom of stack
if(backStackCount == 1){
showUpButton(false);
}
} else {
super.onBackPressed();
}
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
} else if (id == android.R.id.home) {
// Home/Up logic handled by onBackPressed implementation
onBackPressed();
}
return super.onOptionsItemSelected(item);
}
#SuppressWarnings("StatementWithEmptyBody")
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
// Navigation drawer item selection logic goes here
mDrawer.closeDrawer(GravityCompat.START);
return true;
}
private void replaceFragment() {
/**
* Your fragment replacement logic goes here
* e.g.
* FragmentTransaction ft = getFragmentManager().beginTransaction();
* String tag = "MyFragment";
* ft.replace(R.id.content, MyFragment.newInstance(tag), tag).addToBackStack(null).commit();
*/
// The part that changes the hamburger icon to the up icon
showUpButton(true);
}
private void resolveUpButtonWithFragmentStack() {
showUpButton(getSupportFragmentManager().getBackStackEntryCount() > 0);
}
private void showUpButton(boolean show) {
// To keep states of ActionBar and ActionBarDrawerToggle synchronized,
// when you enable on one, you disable on the other.
// And as you may notice, the order for this operation is disable first, then enable - VERY VERY IMPORTANT.
if(show) {
// Remove hamburger
mDrawerToggle.setDrawerIndicatorEnabled(false);
// Show back button
mActionBar.setDisplayHomeAsUpEnabled(true);
// when DrawerToggle is disabled i.e. setDrawerIndicatorEnabled(false), navigation icon
// clicks are disabled i.e. the UP button will not work.
// We need to add a listener, as in below, so DrawerToggle will forward
// click events to this listener.
if(!mToolBarNavigationListenerIsRegistered) {
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onBackPressed();
}
});
mToolBarNavigationListenerIsRegistered = true;
}
} else {
// Remove back button
mActionBar.setDisplayHomeAsUpEnabled(false);
// Show hamburger
mDrawerToggle.setDrawerIndicatorEnabled(true);
// Remove the/any drawer toggle listener
mDrawerToggle.setToolbarNavigationClickListener(null);
mToolBarNavigationListenerIsRegistered = false;
}
// So, one may think "Hmm why not simplify to:
// .....
// getSupportActionBar().setDisplayHomeAsUpEnabled(enable);
// mDrawer.setDrawerIndicatorEnabled(!enable);
// ......
// To re-iterate, the order in which you enable and disable views IS important #dontSimplify.
}
}
Problem 2: Page title - Changing the page titles whenever a fragment in pushed and popped.
Essentially, this can be handled in the onStart for each Fragment i.e. your ListFragment, DetailsFragment and CommentsFragment look something like this:
#Override
public void onStart() {
super.onStart();
// where mText is the title you want on your toolbar/actionBar
getActivity().setTitle(mText);
}
Probably worth having setRetainInstance(true) in the onCreate of your fragments as well.
tl;dr
Watch this:
https://youtu.be/ANpBWIT3vlU
Clone this:
https://github.com/shredderskelton/androidtemplate.
This is a really common problem and one that I've overcome by creating a kind of template project which I use whenever I start a new Android project. The idea is to abstract as much of the logic that handles the back button, the 'hamburger' indicator and fragment management into reusable classes:
Start by creating a BaseActivity and BaseFragment class. This is where you are going to as much of the reusable code as possible.
Lets start with your BaseActivity
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentManager = getSupportFragmentManager();
fragmentHandler = new AddFragmentHandler(fragmentManager);
fragmentManager.addOnBackStackChangedListener(backStackListener);
}
The FragmentManager is the key to owning the back stack, so you need to listen for changes to the back stack from here. The AddFramentHandler is a little class I cooked up to make it easier to add Fragments, from Fragments. More on that later.
#Override
public void onBackPressed() {
if (sendBackPressToDrawer()) {
//the drawer consumed the backpress
return;
}
if (sendBackPressToFragmentOnTop()) {
// fragment on top consumed the back press
return;
}
//let the android system handle the back press, usually by popping the fragment
super.onBackPressed();
//close the activity if back is pressed on the root fragment
if (fragmentManager.getBackStackEntryCount() == 0) {
finish();
}
}
onBackPressed is where most of the magic happens. You notice the plain text formatting of the methods.. I'm a huge Clean Code fan - if you need to write comments, your code isn't clean. Basically you need to really have a central place where you can run to when you're not sure why a back button press is not happening the way you expect. This method is that place.
private void syncDrawerToggleState() {
ActionBarDrawerToggle drawerToggle = getDrawerToggle();
if (getDrawerToggle() == null) {
return;
}
if (fragmentManager.getBackStackEntryCount() > 1) {
drawerToggle.setDrawerIndicatorEnabled(false);
drawerToggle.setToolbarNavigationClickListener(navigationBackPressListener); //pop backstack
} else {
drawerToggle.setDrawerIndicatorEnabled(true);
drawerToggle.setToolbarNavigationClickListener(drawerToggle.getToolbarNavigationClickListener()); //open nav menu drawer
}
}
This is the other key part of the BaseActivity. Basically this method checks whether you are at the root fragment and sets up the indicator accordingly. Notice that it changes the listener depending on how many fragments are in the back stack.
Then there is the BaseFragment:
#Override
public void onResume() {
super.onResume();
getActivity().setTitle(getTitle());
}
protected abstract String getTitle();
The code above shows how the title is handled by the fragments.
Try something like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
if (getSupportActionBar()!=null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
final ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(drawerToggle);
drawerToggle.syncState();
final View.OnClickListener originalToolbarListener = drawerToggle.getToolbarNavigationClickListener();
final View.OnClickListener navigationBackPressListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
getFragmentManager().popBackStack();
}
};
getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if (getFragmentManager().getBackStackEntryCount() > 0) {
drawerToggle.setDrawerIndicatorEnabled(false);
drawerToggle.setToolbarNavigationClickListener(navigationBackPressListener);
} else {
drawerToggle.setDrawerIndicatorEnabled(true);
drawerToggle.setToolbarNavigationClickListener(originalToolbarListener);
}
}
});
// Though below steps are not related but I have included to show drawer close on Navigation Item click.
navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(MenuItem item) {
int id = item.getItemId();
/**
* handle item clicks using id
*/
drawer.closeDrawer(GravityCompat.START);
return true;
}
});
}
Handle the drawer state onBackPressed:
#Override
public void onBackPressed() {
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
To reload previous fragment on back press, always add the fragment transaction to back stack like this:
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
SomeFragment fragmentToBeLoaded = new SomeFragment();
fragmentTransaction.replace(R.id.fragment_container, fragmentToBeLoaded,
fragmentToBeLoaded.getName());
fragmentTransaction.addToBackStack(fragmentToBeLoaded.getName());
fragmentTransaction.commit();
To dynamically change the page title, you can call this from every Fragments onStart or onResume method:
#Override
public void onStart() {
super.onStart();
getActivity().setTitle("Title for fragment");
}
Note: I have considered standard layout declaration and thus I have not included any layouts.
"Page title - Changing the page titles whenever a fragment in pushed and popped"
When you remove a fragment, there is the method isRemoving(). It helps to change title back.
#Override
public void onStop() {
super.onStop();
if (isRemoving()) {
// Change your title here
}
}
"functionality to Menu and Back nav"
Suggestion: we have to rely on the default android navigation system. If we use addToBackStack() for our fragments, in theory we don't have to override onBackPressed() at all.
"App does not redefine the expected function of a system icon (such as the Back button)."
"App supports standard system Back button navigation and does not make use of any custom, on-screen "Back button" prompts."
Core App Quality: https://developer.android.com/distribute/essentials/quality/core.html
"Managing the Hamburger/Back button at left top"
I suggest to use activity instead of 'MainActivityDetailFragment' to avoid complication.
Add this in your MainActivity where you are calling Fragments. getBackStackEntryCount() Return number of fragments in the back stack. where the fragment on the bottom of the stack has index 0. popBackStack() Pop the top Fragment off the back stack
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
if (getSupportFragmentManager().getBackStackEntryCount() == 1) {
getSupportFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
return true;
}
And in your Fragment where you want to go back use this function
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
getActivity().onBackPressed();
}
return true;
}
Ok, after a lot of tests I finally succeeded to setup a good navigation. I needed exactly the same as you, the only difference is that I am using v4 Fragments, but I don't think this will change anything here.
I am not using ActionBarDrawerToggle since the latest examples from Google do not use this component anymore.
The solution below also works for deep navigation: parent activity --> fragment --> fragment etc.
The only change needed in the Fragments is to change the title:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getActivity().setTitle(R.string.targets);
}
In the parent Activity onCreate method, I initialize the following:
mNavigationView = (NavigationView) findViewById(R.id.navigation_view);
setupDrawerContent(mNavigationView);
final Toolbar toolbar = (Toolbar) findViewById(R.id.drawer_toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_menu_24);// Set the hamburger icon
getSupportActionBar().setDisplayHomeAsUpEnabled(true);// Set home button pressable
// Handle the changes on the actionbar
getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
// When no more fragments to remove, we display back the hamburger icon and the original activity title
if (getSupportFragmentManager().getBackStackEntryCount() <= 0) {
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_menu_24);
setTitle(R.string.app_name);
}
// Else displays the back arrow
else {
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_arrow_back_24);
}
}
});
Here is now the code to handle the action on the Home button:
#Override
public boolean onOptionsItemSelected(MenuItem item){
// Close the soft keyboard right away
Tools.setSoftKeyboardVisible(mViewPager, false);
switch (item.getItemId()) {
case android.R.id.home:
// When no more fragments to remove, open the navigation drawer
if (getSupportFragmentManager().getBackStackEntryCount() <= 0) {
mDrawerLayout.openDrawer(GravityCompat.START);
}
// Removes the latest fragment
else {
getSupportFragmentManager().popBackStack();
}
return true;
}
return super.onOptionsItemSelected(item);
}
And finally the code to handle the back press action:
#Override
public void onBackPressed() {
// When no more fragments to remove, closes the activity
if (getSupportFragmentManager().getBackStackEntryCount() <= 0) {
super.onBackPressed();
}
// Else removes the latest fragment
else {
getSupportFragmentManager().popBackStack();
}
}
NOTE: I am using an AppCompatActivity, a NavigationView and the theme Theme.AppCompat.Light.NoActionBar.
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);
}
}
}
}
Is it possible to use a navigation drawer in android but instead of updating fragments, i would like to switch between activities as my means of navigation within the app.
Yes it is possible - it's what I did for my app. I already had a number of activities set up, and rather than convert them all to fragments, I wanted to tailor the navigation drawer to work across all of them. Unfortunately, it's not a quick workaround, so if you have the option of using fragments, I would go with that. But regardless here's how I did it:
Let's say I have 2 activities, both of which I want to have the Navigation Drawer. In the layout.xml for each, I specified a DrawerLayout with the appropriate ListView to hold my navigation options. Essentially, the Navigation drawer is made every time I switch between activities, giving the appearance that it is persisting. To make life a lot easier, I took the common methods required to set up the navigation drawer and put them in their own class: NavigationDrawerSetup.java. That way my activities can use the same custom adapter, etc.
Within this NavigationDrawerSetup.java class, I have the following:
configureDrawer() - this sets up the ActionBar,
ActionBarDrawerToggle, and the required listeners
My custom array adapter (to populate the navigation options within the list)
The selectOptions() method, which handles drawer item clicks
When you set up the navigation drawer within one of your activities, you just create a new NavigationDrawerSetup object and pass in the required layout parameters (like the DrawerLayout, ListView etc). Then you'd call configureDrawer():
navigationDrawer = new NavigationDrawerSetup(mDrawerView, mDrawerLayout,
mDrawerList, actionBar, mNavOptions, currentActivity);
navigationDrawer.configureDrawer();
currentActivity is passed in since the navigation drawer is tied to the activity you are on. You will have to use it when you set up the ActionBarDrawerToggle:
mDrawerToggle = new ActionBarDrawerToggle(currentActivity, // host Activity
mDrawerLayout, /* DrawerLayout object */
R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
R.string.drawer_open, /* "open drawer" description for accessibility */
R.string.drawer_close /* "close drawer" description for accessibility */
)
You will also need to use currentActivity when setting up your custom Adapter:
As for how to switch between activities via the navigation drawer, you can just set up new intents within your selectItem() method:
private void selectItem(int position) {
// Handle Navigation Options
Intent intent;
switch (position) {
case 0:
intent = new Intent(currentActivity, NewActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
currentActivity.startActivity(intent);
break;
case 1:
// etc.
}
Just make sure that your new Activity also has the navigation drawer setup and it should display.
There are a ton of things you can do to customize this method to your own needs, but this is the general structure of how I did it. Hope this helps!
You need a BaseDrawerActivity which implement the Navigation Drawer then extend the BaseDrawerActivity in each activity you need Navigation Drawer.
First create BaseDrawerActivity.java :
public class BaseDrawerActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener{
DrawerLayout drawerLayout;
Toolbar toolbar;
FrameLayout frameLayout;
NavigationView navigationView;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_base_drawer);;
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
frameLayout = (FrameLayout) findViewById(R.id.content_frame);
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawerLayout.setDrawerListener(toggle);
toggle.syncState();
navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
}
#Override
public void onBackPressed() {
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
#Override
public boolean onNavigationItemSelected(MenuItem item) {
int id = item.getItemId();
//to prevent current item select over and over
if (item.isChecked()){
drawerLayout.closeDrawer(GravityCompat.START);
return false;
}
if (id == R.id.nav_camera) {
// Handle the camera action
startActivity(new Intent(getApplicationContext(), CameraActivity.class));
} else if (id == R.id.nav_gallery) {
startActivity(new Intent(getApplicationContext(), GalleryActivity.class));
} else if (id == R.id.nav_slideshow) {
startActivity(new Intent(getApplicationContext(), SlideshowActivity.class));
} else if (id == R.id.nav_manage) {
startActivity(new Intent(getApplicationContext(), ManageActivity.class));
} else if (id == R.id.nav_share) {
startActivity(new Intent(getApplicationContext(), ShareActivity.class));
} else if (id == R.id.nav_send) {
startActivity(new Intent(getApplicationContext(), SendActivity.class));
}
drawerLayout.closeDrawer(GravityCompat.START);
return true;
}
}
then create activity_base_drawer.xml in res/layout folder:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:openDrawer="start">
<include layout="#layout/app_bar_home"/>
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="#layout/nav_header_home"
app:menu="#menu/activity_home_drawer" />
</android.support.v4.widget.DrawerLayout>
where #layout/app_bar_home is:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<FrameLayout android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.design.widget.CoordinatorLayout>
Next you enter your Activities that will have Navigation Drawer such as CameraActivity.java :
public class CameraActivity extends BaseDrawerActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getLayoutInflater().inflate(R.layout.activity_camera, frameLayout);
/**
* Setting title
*/
setTitle("Camera");
}
#Override
protected void onResume() {
super.onResume();
// to check current activity in the navigation drawer
navigationView.getMenu().getItem(0).setChecked(true);
}
}
Where R.layout.activity_camera is your layout for CameraActivity.java.
Then create other Activity like GalleryActivity.java and so on that will have Navigation Drawer:
public class GalleryActivity extends BaseDrawerActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getLayoutInflater().inflate(R.layout.activity_gallery, frameLayout);
// Setting title
setTitle("Gallery");
}
#Override
protected void onResume() {
super.onResume();
navigationView.getMenu().getItem(1).setChecked(true);
}
}
As a little improvement to the solution pointed by #David-Crozier, in order to avoid the overlap of both animations (closing the NavigationDrawer and starting a new activity), you can include a little delay in your method as was done in the iosched app v2014:
private void onNavDrawerItemClicked(final int itemId) {
if (itemId == getSelfNavDrawerItem()) {
mDrawerLayout.closeDrawer(GravityCompat.START);
return;
}
if (isSpecialItem(itemId)) {
goToNavDrawerItem(itemId);
} else {
// launch the target Activity after a short delay, to allow the close animation to play
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
goToNavDrawerItem(itemId);
}
}, NAVDRAWER_LAUNCH_DELAY);
// change the active item on the list so the user can see the item changed
setSelectedNavDrawerItem(itemId);
// fade out the main content
View mainContent = findViewById(R.id.main_content);
if (mainContent != null) {
mainContent.animate().alpha(0).setDuration(MAIN_CONTENT_FADEOUT_DURATION);
}
}
mDrawerLayout.closeDrawer(GravityCompat.START);
}
Here the link for reference: https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/BaseActivity.java
The Fragment manager can be replaced as mentioned in the post:
https://guides.codepath.com/android/fragment-navigation-drawer#alternative-to-fragments
You can inflate a layout instead of using a fragment manager.
In your mobile_navigation file XML.
Add the following.
<activity
android:id="#+id/nav_custom"
android:name="net.larntech.nav.CustomActivity"
android:label="#string/menu_custom"
tools:layout="#layout/activity_custom"
/>
You can find the full tutorial using the link below.
Android Studio Navigation Drawer