Related
I am developing an app where i am using only 1 Main Activity and Multiple fragment, Including ViewPager , Custom video/Image Gallery, Fullscreen Fragment(Without toolbar or bottom navigation button). I am not sure is it good practice or not but i am facing few issues cause of this.
Image above is actual App hierarchy. Following the issue i am facing.
Toolbar doesn't change title of fragment, when press back button or going forward by clicking button or some link.
Navigation hamburger keep showing if i change into back arrow by using: getSupportActionBar().setDisplayHomeAsUpEnabled(true); then back arrow opens drawers but not going back to last fragment.
Fragment State Loss when pressed back button or jumping directly to some fragment.
Whether is it good practice to doing all task within Fragment with Single Activity.
I am also using single Toolbar whole app.
Toolbar.xml
<android.support.v7.widget.Toolbar
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/primary"
app:contentInsetLeft="0dp"
app:contentInsetStart="0dp"
app:contentInsetStartWithNavigation="0dp"
android:fitsSystemWindows="true"
app:layout_collapseMode="pin"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/toolbar_connections"
android:visibility="visible"
android:orientation="horizontal">
<ImageView
android:layout_width="35dp"
android:layout_height="match_parent"
android:id="#+id/appLogo"
android:layout_gravity="center_vertical" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:textSize="22sp"
android:id="#+id/activityTitle"
android:textColor="#color/primary_text"
/>
</LinearLayout>
<LinearLayout
android:id="#+id/toolbar_chat"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:orientation="horizontal">
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_vertical"
android:layout_marginRight="5dp"
android:src="#drawable/baby"
android:id="#+id/User_Image_Toolbar"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="#+id/User_Name_Toolbar"
android:textSize="17sp"
android:textStyle="bold"
android:layout_marginBottom="5dp"
android:text="My Name"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Online"
android:textStyle="italic"
android:id="#+id/User_Online_Status_Toolbar"
android:layout_marginBottom="5dp"
android:layout_below="#+id/User_Name_Toolbar" />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.Toolbar>
Navigation Drawer (Single Activity which parent of all Fragments)
public class Navigation_Drawer extends AppCompatActivity implements UserData {
Toolbar toolbar;
DrawerLayout drawerLayout;
NavigationView navigationView;
String navTitles[];
TypedArray navIcons;
RecyclerView.Adapter recyclerViewAdapter;
ActionBarDrawerToggle drawerToggle;
public static final String TAG = "###Navigation Drawer###";
boolean nextScreen;
//Header
ImageView headerImage,headerUserImage;
TextView userName,userViews;
Context context = this;
//Setting Tabs
ViewPager viewPager;
TabAdapter tabAdapter;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.navigation_drawer);
//Initialise Views
drawerLayout = findViewById(R.id.Navigation_Drawer_Main);
navigationView = findViewById(R.id.nvView);
setupToolbar();
navigationView.setItemIconTintList(null);
setupDrawerContent(navigationView);
settingHeaderItems();
drawerToggle = setupDrawerToggle();
getSupportActionBar().setHomeButtonEnabled(true);
drawerLayout.addDrawerListener(drawerToggle);
viewPager = findViewById(R.id.Navigation_Drawer_ViewPager);
tabAdapter = new TabAdapter(getFragmentManager(), this, false);
viewPager.setAdapter(tabAdapter);
}
public void setupToolbar() {
toolbar = findViewById(R.id.Navigation_Drawer_toolbar);
setSupportActionBar(toolbar);
}
private ActionBarDrawerToggle setupDrawerToggle() {
// NOTE: Make sure you pass in a valid toolbar reference. ActionBarDrawToggle() does not require it
// and will not render the hamburger icon without it.
//return new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close);
return new ActionBarDrawerToggle(this, drawerLayout,toolbar, R.string.drawer_open, R.string.drawer_close);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
// inflater.inflate(R.menu.main_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
//Handle Item Selection
return super.onOptionsItemSelected(item);
}
private void setupDrawerContent(NavigationView navigationView) {
navigationView.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
selectDrawerItem(menuItem);
return true;
}
});
}
public void ChangeFragment_ViewPager(int position, boolean outside) {
if (outside) {
Log.d(TAG, "Change Fragment Calling From Outside");
tabAdapter = new TabAdapter(getFragmentManager(), this, false);
viewPager.setAdapter(tabAdapter);
}
viewPager.setCurrentItem(position);
}
#Override
public void onBackPressed() {
super.onBackPressed();
showSystemUI();
Log.d(TAG, "On Back Pressed");
}
public void showSystemUI() {
if (getWindow() != null) {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getSupportActionBar().show();
} else {
return;
}
}
public void selectDrawerItem(MenuItem menuItem) {
// Create a new fragment and specify the fragment to show based on nav item clicked
Fragment fragment = null;
switch (menuItem.getItemId()) {
case R.id.HeaderImageView:
fragment = new EditProfile();
break;
case R.id.home_Fragment:
Log.d(TAG,"Home Fragment Pressed ");
getFragmentManager().popBackStack(null, android.app.FragmentManager.POP_BACK_STACK_INCLUSIVE);
ChangeFragment_ViewPager(0,false);
// Highlight the selected item has been done by NavigationView
menuItem.setChecked(true);
// Set action bar title
setTitle(menuItem.getTitle());
// Close the navigation drawer
drawerLayout.closeDrawers();
return;
case R.id.ppl_Fragment:
Log.d(TAG,"PPL Fragment Pressed ");
ChangeFragment_ViewPager(1,false);
// Highlight the selected item has been done by NavigationView
menuItem.setChecked(true);
// Set action bar title
setTitle(menuItem.getTitle());
// Close the navigation drawer
drawerLayout.closeDrawers();
return;
case R.id.message_Fragment:
Log.d(TAG,"Message Fragment Pressed ");
fragment = new Messages_Fragment();
break;
case R.id.addMedia_Fragment:
Log.d(TAG,"Add Media Fragment Pressed ");
fragment = new UserProfile_Photos();
break;
case R.id.invite_Fragment:
Log.d(TAG,"Invite Fragment Pressed ");
//fragmentClass = fragment_1.class;
onInviteClicked();
// Highlight the selected item has been done by NavigationView
menuItem.setChecked(true);
// Set action bar title
setTitle(menuItem.getTitle());
// Close the navigation drawer
drawerLayout.closeDrawers();
return;
case R.id.setting_Fragment:
Log.d(TAG,"Setting Fragment Pressed ");
fragment = new Setting_NavigationDrawer();
break;
case R.id.help_Fragment:
Log.d(TAG,"Help Fragment Pressed ");
//fragmentClass = fragment_1.class;
fragment=new FullScreen_WebView();
Bundle urlToSend=new Bundle();
urlToSend.putString("webViewURL","http://boysjoys.com/test/Android/Data/help.php");
//urlToSend.putString("webViewURL",chat_wrapper.getGoogleSearch().get(2));
fragment.setArguments(urlToSend);
FragmentTransaction transaction=((Activity)context).getFragmentManager().beginTransaction();
//fragmentTrasaction.replace(R.id.Chat_Screen_Main_Layout,gallery);
//transaction.replace(R.id.Chat_Screen_Main_Layout,fullScreen_webView);
transaction.replace(R.id.Navigation_Main_Layout,fragment);
transaction.addToBackStack(null);
transaction.commit();
// Highlight the selected item has been done by NavigationView
menuItem.setChecked(true);
// Set action bar title
setTitle(menuItem.getTitle());
// Close the navigation drawer
drawerLayout.closeDrawers();
return;
case R.id.signOut_Fragment:
new CheckLoginStatus(this, 0).execute();
new Send_Session_Logout(this).execute();
drawerLayout.closeDrawers();
return;
}
FragmentTransaction fragmentTransaction=getFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.Navigation_Main_Layout, fragment);
fragmentTransaction.setCustomAnimations(R.animator.enter_anim,R.animator.exit_anim);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
// Highlight the selected item has been done by NavigationView
menuItem.setChecked(true);
// Set action bar title
setTitle(menuItem.getTitle());
// Close the navigation drawer
drawerLayout.closeDrawers();
}
private void settingHeaderItems(){
View HeaderLayout = navigationView.inflateHeaderView(R.layout.navigation_header_image);
//Main Screen Tabs With VIew Pager
headerImage = HeaderLayout.findViewById(R.id.HeaderImageView);
headerImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FragmentTransaction fragmentTransaction=getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.Navigation_Main_Layout, new EditProfile());
fragmentTransaction.setCustomAnimations(R.animator.enter_anim,R.animator.exit_anim);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
drawerLayout.closeDrawers();
}
});
headerUserImage = HeaderLayout.findViewById(R.id.HeaderProfilePicture);
userName = HeaderLayout.findViewById(R.id.myImageViewText);
userViews = HeaderLayout.findViewById(R.id.profileViews);
if (Session.getUserCover().equals("Invalid Image")){
headerImage.setBackgroundResource(R.drawable.cam_icon);
}else {
Log.d(TAG,"Path Of Cover Photo "+Session.getUserCover());
Bitmap coverPhoto= BitmapFactory.decodeFile(Session.getUserCover());
headerImage.setImageBitmap(coverPhoto);
// Glide.with(context).load(Session.getUserCover()).apply(new RequestOptions().skipMemoryCache(true).onlyRetrieveFromCache(false).diskCacheStrategy(DiskCacheStrategy.NONE)).into(holder.HeaderImage);
}
Bitmap bitmap = BitmapFactory.decodeFile(Session.getUserImage());
userName.setText(Session.getUserFname()+" "+Session.getUserLname());
headerUserImage.setImageBitmap(bitmap);
if (Session.getProfileCounter().equals("0")){
userViews.setText("No Profile VIsits");
}
else {
userViews.setText("Profile views: "+ Session.getProfileCounter());
}
}
#Override
protected void onPostCreate(#Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
drawerToggle.syncState();
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
drawerToggle.onConfigurationChanged(newConfig);
}
}
I tired alot to resolve this issue and after months of googling and stackoverflow i m still stuck in same issue.
Issue example of Point 1:- When navigation drawer load first everything looks good, view pager changes title as per fragment. then if i click on Navigation Drawer's Menu which also open another fragment (For Ex: Recent Message). then title change successfully but when i press back button or trying to press home button which calls viewpager then title remain same as before i.e. Recent Message.
Setting Title in each fragment like this.
toolbar = (Toolbar) getActivity().findViewById(R.id.Navigation_Drawer_toolbar);
ImageView appLogo = toolbar.findViewById(R.id.appLogo);
TextView fragmentTitle = toolbar.findViewById(R.id.activityTitle);
appLogo.setImageResource(DrawableImage);
fragmentTitle.setText(Title);
If application have to use Navigation Drawer which should be present in all views, then Fragment should be used. And its not a bad practice.
Toolbar doesn't change title of fragment, when press back button or
going forward by clicking button or some link.
Create a method in Base Activity
public void setFragmentTitle(String title){
if(!TextUtils.isEmpty(title))
mTitleText.setText(title);
}
Access this method from your individual Fragments in onCreateView
((LandingActivity) getActivity()).setFragmentTitle(getActivity().getString(R.string.fragment_title));
Navigation hamburger keep showing if i change into back arrow by using: getSupportActionBar().setDisplayHomeAsUpEnabled(true); then back arrow opens drawers but not going back to last fragment.
Use onOptionItemSelected on click of android.R.id.home , pop the current Fragment
Fragment State Loss when pressed back button or jumping directly to some fragment
You have to mention the values which needs to be persisted and repopulate it.
public class ActivityABC....{
private String mFName;
private TableSelectFragment mFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getSupportFragmentManager();
if (savedInstanceState != null) {
mFragment=(TableSelectFragment)fm.getFragment(savedInstanceState,"TABLE_FRAGMENT");
mFName = savedInstanceState.getString("FNAMETAG");
}else{
mFragment = new TableSelectFragment();
fm.beginTransaction().add(R.id.content_frame,mFragment,"TABLE_FRAGMENT").commit();
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getSupportFragmentManager().putFragment(outState,"TABLE_FRAGMENT",mFragment);
}
}
In your Fragment
TableSelectFragment{
....
private String mFName;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
outState.putString("FNAMETAG", mFName);
super.onSaveInstanceState(outState);
}
}
EDIT 1: For Fragment Title not getting updated in BackButton press
While adding Fragment to backstack , do the following.
In your parent Activity
FragmentManager fragMan = getSupportFragmentManager();
FragmentTransaction fragTrans = fragMan.beginTransaction();
LandingFrag landingFrag = LandingFrag.newInstance();
fragTrans.replace(R.id.landing_view, landingFrag,"LandingFrag");
fragTrans.addToBackStack(null);
fragTrans.commit();
landingFrag.setUserVisibleHint(true);
Now override onBackPressed in parent Activity
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
.. POP Fragment Backstack here
Fragment fragment = getActiveFragment();
if(fragment instanceof LandingFrag)
{
LandingFrag landingFrag = (LandingFrag)fragment;
landingFrag.setUserVisibleHint(true);
}
}
public Fragment getActiveFragment() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
return null;
}
Fragment fragment=null;
int trackBackValue = 1;//INCREASE OR DECREASE ACCORDING TO YOUR BACK STACK
try {
fragment = getSupportFragmentManager().getFragments().get(getSupportFragmentManager().getBackStackEntryCount() - trackBackValue);
} catch (Exception e) {
}
return fragment;
}
Now in LandingFrag
public class LandingFrag...
{
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setUserVisibleHint(false);
.....
}
#Override
public void onViewStateRestored(#Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
................
((LandingActivity) getActivity()).setFragmentTitle("Current Fragment Title");
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser){
try {
((LandingActivity) getActivity()).setFragmentTitle("Current Fragment Title");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Whether is it good practice to doing all task within Fragment with
Single Activity.
It is a good practice to use fragments when you use with navigation drawers, tabs or bottom navigation.
Other than that, fragments are mostly preferred for reusable UI with different data.
You are doing all task within fragment with Single Activity because you are using navigation draw and tabs, so It is a good practice, I'd say.
Toolbar doesn't change title of fragment, when press back button or
going forward by clicking button or some link.
You need to manually set the title for fragment whenever you are adding/replacing new fragment OR
on back press(backstack change), through Fragment only or using OnBackStackChangedListener as mentioned in docs.
You are just setting them while you are clicking through navigation items and not while clicking button or some link
Navigation hamburger keep showing if i change into back arrow by
using: getSupportActionBar().setDisplayHomeAsUpEnabled(true); then
back arrow opens drawers but not going back to last fragment.
That too you'd need to handle manually. using onOptionItemSelected on click of android.R.id.home item.
Like poping fragments out from stack.
The cause of your title persistence on back is setting title from Activity.
ie using this setTitle(menuItem.getTitle()); instead set them through Fragment or if you are setting them trough Fragment remove from selectDrawerItem method.
Also at the end of selectDrawerItem method you are using fragmentTransaction.add instead use fragmentTransaction.replace
I started creating of app which use one activity (Navigation Drawer) and many fragments.
But I unable to use toolbar back button to navigate back from fragments. Hardware back button works perfectly. I know that I need to override onOptionsItemSelected, catch android.R.id.home, check if there are something in back stack and than pop it. After changing fragment, "burger" button changes to "back arrow", but when I click on it onOptionsItemSelected never fired, just opens the NavigationDrawer menu.
Here the code from activity:
public class NavDrawerActivity extends AppCompatActivity implements ... {
NavigationView navigationView;
BottomNavigationView bottomNavigationView;
ActionBarDrawerToggle toggle;
FragmentManager fragmentManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nav_drawer);
fragmentManager = getSupportFragmentManager();
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
final DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
// Set back button
fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if (fragmentManager.getBackStackEntryCount() > 0) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} else {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
toggle.syncState();
}
}
});
// Load default fragment
changeFragment(new HomeFragment(), false);
navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
View headerLayout = navigationView.getHeaderView(0);
bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(this);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Toast.makeText(this, "Back pressed", Toast.LENGTH_SHORT)
.show();
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START))
drawer.closeDrawer(GravityCompat.START);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStack();
} else {
super.onBackPressed();
}
}
private void changeFragment(Fragment fm, boolean addToBackStack)
{
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.replace(R.id.frame_layout_content, fm);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
if (addToBackStack) ft.addToBackStack(null);
ft.commit();
}
}
And how I change (replace) fragments from HomeFragment:
IndexDetailFragment newFragment = new IndexDetailFragment();
Bundle args = new Bundle();
args.putString(IndexDetailFragment.ARG_INDEX_ID, id);
newFragment.setArguments(args);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
transaction.replace(R.id.frame_layout_content, newFragment);
transaction.addToBackStack(null);
transaction.commit();
setNavigationOnClick() on the toolbar after setSupportActionBar(toolbar) :
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
Toast.makeText(getActivity(), "Back clicked!",
Toast.LENGTH_SHORT).show();
}
});
I have made a small app for reference
FirstFragment
public class FirstFragment extends Fragment {
public FirstFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_first, container, false);
}
}
SecondFragment
public class SecondFragment extends Fragment {
public SecondFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_second, container, false);
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
private Toolbar mToolbar;
private ActionBarDrawerToggle drawerToggle;
private DrawerLayout mDrawerLayout;
private String TAG = "MainActivity";
private FragmentManager mFragmentManager;
private NavigationView mNavigationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = (Toolbar) findViewById(R.id.toolbar);
if (mToolbar != null) {
setSupportActionBar(mToolbar);
}
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer);
drawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close) {
#Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
}
#Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
}
};
mDrawerLayout.addDrawerListener(drawerToggle);
mNavigationView = findViewById(R.id.navigation);
mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.first:
changeFragment(new FirstFragment(), true);
return true;
case R.id.second:
changeFragment(new SecondFragment(), true);
return true;
}
return false;
}
});
mFragmentManager = getSupportFragmentManager();
changeFragment(new FirstFragment(), true);
}
#Override
protected void onPostCreate(#Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
if (drawerToggle != null) {
drawerToggle.syncState();
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
Log.i(TAG, "onOptionsItemSelected: Home Button Clicked");
if (mDrawerLayout.isDrawerOpen(Gravity.START)) {
mDrawerLayout.closeDrawer(Gravity.START);
} else {
mDrawerLayout.openDrawer(Gravity.START);
}
}
return super.onOptionsItemSelected(item);
}
#Override
public void onBackPressed() {
if (mDrawerLayout.isDrawerOpen(Gravity.START)) {
mDrawerLayout.closeDrawer(Gravity.START);
}
if (mDrawerLayout.isDrawerOpen(Gravity.START)) {
mDrawerLayout.closeDrawer(Gravity.START);
} else if (mFragmentManager.getBackStackEntryCount() > 0) {
mFragmentManager.popBackStack();
} else {
super.onBackPressed();
}
}
private void changeFragment(Fragment fragment, boolean needToAddBackstack) {
FragmentTransaction mFragmentTransaction = mFragmentManager.beginTransaction();
mFragmentTransaction.replace(R.id.FRAME_CONTENT, fragment);
mFragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
if (needToAddBackstack)
mFragmentTransaction.addToBackStack(null);
mFragmentTransaction.commit();
}
}
activity_main
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="#dimen/abc_action_bar_default_height_material"
android:background="#color/colorPrimaryDark"
/>
<FrameLayout
android:id="#+id/FRAME_CONTENT"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="#dimen/abc_action_bar_default_height_material" />
</FrameLayout>
<android.support.design.widget.NavigationView
android:id="#+id/navigation"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:layout_marginTop="#dimen/abc_action_bar_default_height_material"
app:menu="#menu/drawermenu" />
</android.support.v4.widget.DrawerLayout>
I had the same issue.
I finally solved by adding this in the onCreate method.
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if(getSupportFragmentManager().getBackStackEntryCount() == 0){
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
menuToggle.setDrawerIndicatorEnabled(true);
}else{
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
menuToggle.setDrawerIndicatorEnabled(false);
}
}
});
Hope it helps
Simplest solution for Kotlin Developers
Just add this in your root activity where fragments resist
if (supportFragmentManager.backStackEntryCount > 0) {
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
toolbar.setNavigationOnClickListener {
if (supportFragmentManager.backStackEntryCount > 0) {
super.onBackPressed()
} else {
supportActionBar!!.setDisplayHomeAsUpEnabled(false)
drawerLayout.addDrawerListener(toggle)
toggle.syncState()
drawerLayout.openDrawer(GravityCompat.START)
}
}
} else {
supportActionBar!!.setDisplayHomeAsUpEnabled(false)
drawerLayout.addDrawerListener(toggle)
toggle.syncState()
}
Here, whenever setDisplayHomeAsUpEnabled is set true , I am showing back button. and on cliking it, I am calling super.onBackPressed() which is similar to what your back button does!
Add below code in onCreate() method
that's work for me
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, " Back Pressed ", Toast.LENGTH_SHORT).show();
}
});
} else {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
drawerToggle = new ActionBarDrawerToggle(MainActivity.this, drawerLayout, toolbar,
R.string.app_name, R.string.app_name);
drawerLayout.addDrawerListener(drawerToggle);
drawerToggle.syncState();
}
}
});
The thing that solved me the issue was this:
I used the code that came automatically with the NavigationDrawerActivity template, and it was something like this:
NavigationView navigationView = _binding.navView;
// Passing each menu ID as a set of Ids because each menu should be considered as top level destinations:
_AppBarConfiguration = new AppBarConfiguration.Builder(R.id.nav_x, R.id.nav_y, R.id.nav_z)
.setOpenableLayout(_binding.drawerLayout)
.build();
_navController = Navigation.findNavController(this, R.id.nav_host_content_main);
NavigationUI.setupActionBarWithNavController(this, _navController, _AppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, _navController);
And I struggled a lot with many tricks and hacks, and nothing was working for me.
At some point, I have noticed that the fragments that I want to enable the back button are "being considered as top level destinations", exactly what the comment says in the code above. Then I removed those sub-fragments and BOOM that was it, with not a single hack or line of code needed, this is provided out of the box, I just needed to pay attention to it.
So in my case, I changed to the below code:
NavigationView navigationView = _binding.navView;
// Passing each menu ID as a set of Ids because each menu should be considered as top level destinations:
_AppBarConfiguration = new AppBarConfiguration.Builder(R.id.nav_x)
.setOpenableLayout(_binding.drawerLayout)
.build();
_navController = Navigation.findNavController(this, R.id.nav_host_content_main);
NavigationUI.setupActionBarWithNavController(this, _navController, _AppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, _navController);
: Just removed R.id.nav_y, R.id.nav_z in the AppBarConfiguration
I struggled with this for a while. I got it to work by:
setting my the drawer layout in MainActivity.kt:
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
findViewById<NavigationView>(R.id.nav_view).setupWithNavController(navController)
appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout)
Include the toolbar layout in each of the fragments that I need the Toolbar:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
/>
</com.google.android.material.appbar.AppBarLayout>
</LinearLayout>
Add the following code to the home fragment of the drawer, the one from which other fragments are called using the drawer. This gets the hamburguer icon to work, onside the onViewCreated method:
var myToolbar = requireActivity()
.findViewById<androidx.appcompat.widget.Toolbar>(R.id.toolbar)
myToolbar.inflateMenu(R.menu.menu)
val drawerLayout = requireActivity().findViewById<DrawerLayout>(R.id.drawer_layout)
val toggle = ActionBarDrawerToggle(
activity, drawerLayout, myToolbar,
R.string.navigation_drawer_open, R.string.navigation_drawer_close
)
toggle.setDrawerIndicatorEnabled(true)
drawerLayout.addDrawerListener(toggle)
toggle.syncState()
Add the following code in the kotlin code for the fragments that are after the home fragment, this will get the back arrow:
val navController = findNavController()
val appBarConfiguration = AppBarConfiguration(navController.graph)
view.findViewById<Toolbar>(R.id.toolbar)
.setupWithNavController(navController, appBarConfiguration)
In the destination fragment add the following code in onCreateView Method:
activity?.onBackPressedDispatcher?.addCallback(
viewLifecycleOwner, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
findNavController().navigate(
[back fragment directions] )
}
})
Hope this helps everyone struggling with this :)
I am working on a bottom navigation bar, but I am not getting perfectly bottom navigation bar.
My MainActivity class:
public class MainActivity extends AppCompatActivity {
private static final String SELECTED_ITEM = "selected_item";
private BottomNavigationView bottomNavigationView;
private Toolbar toolbar;
private MenuItem menuItemSelected;
private int mMenuItemSelected;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
selectFragment(item);
return true;
}
});
//Always load first fragment as default
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, new AnnouncementFragment());
fragmentTransaction.commit();
if (savedInstanceState != null) {
mMenuItemSelected = savedInstanceState.getInt(SELECTED_ITEM, 0);
menuItemSelected = bottomNavigationView.getMenu().findItem(mMenuItemSelected);
} else {
menuItemSelected = bottomNavigationView.getMenu().getItem(0);
}
selectFragment(menuItemSelected);
}
private void selectFragment(MenuItem item) {
Fragment fragment = null;
Class fragmentClass;
switch (item.getItemId()) {
case R.id.action_announcement:
fragmentClass = AnnouncementFragment.class;
break;
case R.id.action_menu:
fragmentClass = MenuFragment.class;
break;
case R.id.action_menu_reports:
fragmentClass = ReportFragment.class;
break;
case R.id.action_setting:
fragmentClass = SettingFragment.class;
break;
default:
fragmentClass = AnnouncementFragment.class;
}
try {
fragment = (Fragment) fragmentClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.frameLayout, fragment).commit();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(SELECTED_ITEM, mMenuItemSelected);
super.onSaveInstanceState(outState);
}
And my back pressed also not working properly:
#Override
public void onBackPressed() {
MenuItem homeItem = bottomNavigationView.getMenu().getItem(0);
if (mMenuItemSelected != homeItem.getItemId()) {
selectFragment(homeItem);
} else {
super.onBackPressed();
}
}
How should I do that because bottom menu has uneven distribution on bar. How to properly maintain the menu space without uneven distribution.
Here I am attaching my result which I obtain on AVD
According to the guidelines for Material Design
On Android, the Back button does not navigate between bottom
navigation bar views.
EDIT: Material Design link no longer mentions back button behavior.
Pressing the back button you can quit the application, which is the default behavior, such as in Google Photo...
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.content, fragment);
// note: there is NOT a addToBackStack call
fragmentTransaction.commit();
...or lead the user to the home section and then, if pushed again, at the exit.
Personally I find this last pattern much better.
To get it without override onBackPressed you need to identify the home fragment and differentiate it from all the others
navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
viewFragment(new HomeFragment(), FRAGMENT_HOME);
return true;
case R.id.navigation_page_1:
viewFragment(new OneFragment(), FRAGMENT_OTHER);
return true;
case R.id.navigation_page_2:
viewFragment(new TwoFragment(), FRAGMENT_OTHER);
return true;
}
return false;
}
});
What you have to do now is write the viewfragment method that have to:
Know how many fragments there are in the stack before the commit
If the fragment is not "home type", save it to the stack before
the commit
Add an OnBackStackChangedListener that when the stack decreases,
(i.e. when I pressed back ), delete all the fragments that are
not "home type" (POP_BACK_STACK_INCLUSIVE) , bringing us to the home fragment
Below the full method with comments
private void viewFragment(Fragment fragment, String name){
final FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.content, fragment);
// 1. Know how many fragments there are in the stack
final int count = fragmentManager.getBackStackEntryCount();
// 2. If the fragment is **not** "home type", save it to the stack
if( name.equals( FRAGMENT_OTHER) ) {
fragmentTransaction.addToBackStack(name);
}
// Commit !
fragmentTransaction.commit();
// 3. After the commit, if the fragment is not an "home type" the back stack is changed, triggering the
// OnBackStackChanged callback
fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
// If the stack decreases it means I clicked the back button
if( fragmentManager.getBackStackEntryCount() <= count){
// pop all the fragment and remove the listener
fragmentManager.popBackStack(FRAGMENT_OTHER, POP_BACK_STACK_INCLUSIVE);
fragmentManager.removeOnBackStackChangedListener(this);
// set the home button selected
navigation.getMenu().getItem(0).setChecked(true);
}
}
});
}
Try this
#Override
public void onBackPressed() {
BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.navigation);
int seletedItemId = bottomNavigationView.getSelectedItemId();
if (R.id.home != seletedItemId) {
setHomeItem(MainActivity.this);
} else {
super.onBackPressed();
}
}
public static void setHomeItem(Activity activity) {
BottomNavigationView bottomNavigationView = (BottomNavigationView)
activity.findViewById(R.id.navigation);
bottomNavigationView.setSelectedItemId(R.id.home);
}
#Override
public void onBackPressed() {
BottomNavigationView mBottomNavigationView = findViewById(R.id.navigation);
if (mBottomNavigationView.getSelectedItemId() == R.id.navigation_home)
{
super.onBackPressed();
finish();
}
else
{
mBottomNavigationView.setSelectedItemId(R.id.navigation_home);
}
}
This is maybe a little late but I think the best way to do it is as simple as this.
#Override
public void onBackPressed() {
if (mBottomNavigationView.getSelectedItemId() == R.id.action_home) {
super.onBackPressed();
} else {
mBottomNavigationView.setSelectedItemId(R.id.action_home);
}
}
I hope it helps and happy coding :)
onBackPressed did not worked for me. So this I used.
#Override
protected void onResume() {
super.onResume();
bottomNavigationView.getMenu().getItem(0).setChecked(true);
}
The best way is: when in home button the app is closed and when in another button back in home button.
Below I put my code :
first i'm load home button in navigation View :
private void loadFragment(Fragment fragment) {
Toast.makeText(this, "load", Toast.LENGTH_SHORT).show();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_container, fragment, TAG_FRAGMENT);
transaction.commit();
}
and remember dont call addToBackStack() .
and then handle the situation by onBackPressed() :
#Override
public void onBackPressed() {
if (navigation.getSelectedItemId() == R.id.bottomAkhbar) {
super.onBackPressed();
} else {
navigation.setSelectedItemId(R.id.bottomAkhbar);
}
}
I had the same problem so I solved my problem using this method:
In my main activity, I have a bottom nav bar and a nav drawer, I need to sync items in my drawer and bottom nav:
I created a method for my main fragment and the others:
my main fragment replacer:
public void MainFragmentChanger(final Fragment fragment, final String TAG){
if (main_page_fragment != null){
fragmentTransaction = myFragmentManager.beginTransaction();
fragmentTransaction.remove(main_page_fragment).commit();
}
if (main_drawer.isDrawerOpen()){
main_drawer.closeDrawer();
}
new Handler().post(new Runnable() {
#Override
public void run() {
main_page_fragment = fragment;
main_page_fragment.setRetainInstance(true);
fragmentTransaction = myFragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.main_container, main_page_fragment,TAG);
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
fragmentTransaction.commitAllowingStateLoss();
}
});
}
and this is for my other fragment replacer:
public void changeBottomFragment(final Fragment fragment, final String TAG){
if (main_drawer.isDrawerOpen()){
main_drawer.closeDrawer();
}
new Handler().post(new Runnable() {
#Override
public void run() {
main_page_fragment = fragment;
main_page_fragment.setRetainInstance(true);
fragmentTransaction = myFragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.main_container, main_page_fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
fragmentTransaction.commitAllowingStateLoss();
}
});
}
so after that, I need to sync items in both drawer and bar:
Note I use material navigation drawer by Mike Penz, and com.ashokvarma.bottomnavigation.BottomNavigationBar for nav bar.
here is the method for that purpose:
public void changeNavAndBarStats(String tag){
if (tag == "flash"){
bottomNavigationBar.selectTab(2,false);
main_drawer.setSelection(flashcards.getIdentifier(),false);
}else if (tag == "dic"){
bottomNavigationBar.selectTab(3,false);
main_drawer.setSelection(dictionary.getIdentifier(),false);
}else if (tag == "Home"){
bottomNavigationBar.selectTab(0,false);
main_drawer.setSelection(home.getIdentifier(),false);
}
}
So I call my fragments Like this:
MainFragmentChanger(new MainPageFragment(),"Home");
bottomNavigationBar.selectTab(0,false);
changeBottomFragment(new FlashCardFragment(),"flash");
bottomNavigationBar.selectTab(2,false);
changeBottomFragment(new TranslateFragment(),"dic");
bottomNavigationBar.selectTab(3,false);
At the end I call changeNavAndBarStatus in my fragment's onResume method:
((MainPageActivity)getContext()).changeNavAndBarStats("flash");
That's it! you are good to go!
Try this to achieve the following:
on back press:
from home fragment exit the app.
from other fragments goto home fragment.
//On Back Press if we are at a Fragment other than the Home Fragment it will navigate back to the
// Home Fragment. From Home Fragment it will exit the App.
#Override
public void onBackPressed() {
int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
if (backStackEntryCount == 0) {
super.onBackPressed();
} else {
goHome();
}
}
public void goHome() {
//Following code will set the icon of the bottom navigation to active
final BottomNavigationView mBottomNav = findViewById(R.id.nav_view);
MenuItem homeItem = mBottomNav.getMenu().getItem(0);
mBottomNav.setSelectedItemId(homeItem.getItemId());
getSupportFragmentManager().popBackStackImmediate();
//To delete all entries from back stack immediately one by one.
int backStackEntry = getSupportFragmentManager().getBackStackEntryCount();
for (int i = 0; i < backStackEntry; i++) {
getSupportFragmentManager().popBackStackImmediate();
}
//To navigate to the Home Fragment
final HomeFragment homeFragment = new HomeFragment();
FragmentTransaction myFragmentTransaction = getSupportFragmentManager().beginTransaction();
myFragmentTransaction.replace(R.id.nav_host_fragment, homeFragment, "HomeFrag Tag");
myFragmentTransaction.commit();
}
You can try this /
its worked for me
public class MainActivity extends BaseActivity {
private HomeFragment homeFragment = new HomeFragment();
private CartFragment cartFragment = new CartFragment();
private ProfileFragment profileFragment = new ProfileFragment();
private BottomNavigationView bottomNavigationView;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null){
setContentView(R.layout.activity_main);
bottomNavigationView = findViewById(R.id.bottom_nav);
bottomNavigationView.setSelectedItemId(R.id.btnHome);
FragmentTransaction homeFm = getSupportFragmentManager().beginTransaction();
homeFm.replace(R.id.fragment_container, homeFragment);
homeFm.commit();
setupView();
}
}
private void setupView() {
bottomNavigationView.setOnNavigationItemSelectedListener(item -> {
{
switch (item.getItemId()) {
case R.id.btnHome:
loadFragment(homeFragment);
return true;
case R.id.btnCart:
loadFragment(cartFragment);
return true;
case R.id.btnProfile:
loadFragment(profileFragment);
return true;
}
return false;
}
});
}
private void loadFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
#Override
public void onBackPressed() {
if (bottomNavigationView.getSelectedItemId() == R.id.btnHome)
{
finish();
}
else
{
bottomNavigationView.setSelectedItemId(R.id.btnHome);
}
}}
Use addToBackStack Method when calling a fragment like this,
getSupportFragmentManager().beginTransaction().addToBackStack(null).add(R.id.content_home_nav,newFragment).commit();
Use this code in your onBackPressed Method
if (getSupportFragmentManager().getBackStackEntryCount() > 0 ){
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
For your requirement you would be working with fragments on navigation for this you can use Tablayout with view pager and make bottom navigation.
<android.support.design.widget.TabLayout
android:id="#+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="60dp"></android.support.design.widget.TabLayout>
and then setup viewpager with tab layout and add icon to tablayout in your activity
tabLayout = (TabLayout) findViewById(R.id.tab_layout);
viewPager = (ViewPager) findViewById(R.id.controller_pager);
viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
viewPager.setOffscreenPageLimit(4);
tabLayout.setupWithViewPager(viewPager);
tabLayout.getTabAt(0).setIcon(R.drawable.selector_home);
tabLayout.getTabAt(1).setIcon(R.drawable.selector_contact);
tabLayout.getTabAt(2).setIcon(R.drawable.selector_profile);
tabLayout.getTabAt(3).setIcon(R.drawable.selector_settings);
now handle all things on the click of tablayout and it will work fine
tabLayout.addOnTabSelectedListener(this);
I was facing the same problem but after doing this I got the solution
First Paste this code in your main activity (where you are using Bottom navigation bar)
BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.navigation);
BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);
And then create a class named BottomNavigationViewHelper and paste the following code.
public class BottomNavigationViewHelper {
public static void disableShiftMode(BottomNavigationView view) {
BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0);
try {
Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
shiftingMode.setAccessible(true);
shiftingMode.setBoolean(menuView, false);
shiftingMode.setAccessible(false);
for (int i = 0; i < menuView.getChildCount(); i++) {
BottomNavigationItemView item = (BottomNavigationItemView) menuView.getChildAt(i);
//noinspection RestrictedApi
item.setShiftingMode(false);
// set once again checked value, so view will be updated
//noinspection RestrictedApi
item.setChecked(item.getItemData().isChecked());
}
} catch (NoSuchFieldException e) {
Log.e("BNVHelper", "Unable to get shift mode field", e);
} catch (IllegalAccessException e) {
Log.e("BNVHelper", "Unable to change value of shift mode", e);
}
}
}
Hope It helps
Try this.
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
// Gets the previous global stack count
int previousStackCount = mPreviousStackCount;
// Gets a FragmentManager instance
FragmentManager localFragmentManager = getSupportFragmentManager();
// Sets the current back stack count
int currentStackCount = localFragmentManager.getBackStackEntryCount();
// Re-sets the global stack count to be the current count
mPreviousStackCount = currentStackCount;
boolean popping = currentStackCount < previousStackCount;
if(popping){
bottomNavigationView.getMenu().getItem(0).setChecked(true);
}
}
});
Please try this solution.
I have made changes in your code given in question.
I have assumed that on back pressing first time your app will come back to home fragment (in your case Announcement fragment) and if you back press again, the app will close.
This flow will also reflect in bottom navigation bar.
public class MainActivity extends AppCompatActivity {
private static final String BACK_STACK_ROOT_TAG = "root_home_fragment";
private static final String SELECTED_ITEM = "selected_item";
private Fragment fragment;
private FragmentManager fragmentManager;
private BottomNavigationView bottomNavigationView;
private Toolbar toolbar;
private MenuItem menuItemSelected;
private int mMenuItemSelected;
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
boolean selected = false;
switch (item.getItemId()) {
case R.id.action_announcement:
fragment = AnnouncementFragment.newInstance();
selected = true;
break;
case R.id.action_menu:
fragment = MenuFragment.newInstance();
selected = true;
break;
case R.id.action_menu_reports:
fragment = ReportFragment.newInstance();
selected = true;
break;
case R.id.action_setting:
fragment = SettingFragment.newInstance();
selected = true;
}
if(fragment !=null){
fragmentManager = getFragmentManager();
switch (item.getItemId()) {
case R.id.action_announcement:
// Pop every fragment from backstack including home fragment.
fragmentManager.popBackStack(BACK_STACK_ROOT_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
fragmentManager.beginTransaction()
.replace(R.id.content, fragment)
.addToBackStack(BACK_STACK_ROOT_TAG)
.commit();
break;
default:
fragmentManager.beginTransaction()
.replace(R.id.content, fragment)
.addToBackStack(null)
.commit();
break;
}
}
return selected;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
//Always load first fragment as default
bottomNavigationView.setSelectedItemId(R.id.action_announcement);
if (savedInstanceState != null) {
mMenuItemSelected = savedInstanceState.getInt(SELECTED_ITEM, 0);
menuItemSelected = bottomNavigationView.getMenu().findItem(mMenuItemSelected);
} else {
menuItemSelected = bottomNavigationView.getMenu().getItem(0);
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(SELECTED_ITEM, mMenuItemSelected);
super.onSaveInstanceState(outState);
}
public void onBackPressed() {
int count = getFragmentManager().getBackStackEntryCount();
if(count >1){
// We have lots of fragment on backstack to be popped.
// Pop till the root fragment.
getFragmentManager().popBackStack(BACK_STACK_ROOT_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
bottomNavigationView.setSelectedItemId(R.id.action_announcement);
}
else{
// Close the application when we are on home fragment.
supportFinishAfterTransition();
}
}
}
Most time when you, press back button, old fragment in the back stack,
are recalled. therefore the system call this onCreateView() method
there add this code
val bottomNav = activity?.findViewById<BottomNavigationView>(R.id.activity_main_bottom_navigation)
bottomNav?.selectedItemId = R.id.the_id_of_the_icon__that_represent_the_fragment
I did this after trying all and everything and at last it worked -_- .I have pasted this 2 override method in my each and every activity that i am surfing through my bottom navigation.
#Override
protected void onResume() {
super.onResume();
bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation_Menu_name);
bottomNavigationView.getMenu().getItem(Menu_item_position).setChecked(true);
}
#Override
protected void onRestart() {
super.onRestart();
bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation_Menu_name);
bottomNavigationView.getMenu().getItem(Menu_item_position).setChecked(true);
}
Late answer but here it goes.
Let's say you have a BottomNavigation inside MainActivity with 4 Fragments.
FragmentA
FragmentB
FragmentC
FragmentD
If your are adding each fragment to backstack like so:
With kotlin:
main_bottom_navigation.setOnNavigationItemSelectedListener { item ->
var fragment: Fragment? = null
when (item.itemId) {
R.id.nav_a -> fragment = FragmentA()
R.id.nav_b -> fragment = FragmentB()
R.id.nav_c -> fragment = FragmentC()
R.id.nav_d -> fragment = FragmentD()
}
supportFragmentManager
.beginTransaction()
.setCustomAnimations(R.anim.abc_fade_in, R.anim.abc_fade_out)
.replace(R.id.home_content, fragment!!)
.addToBackStack(fragment.tag)
.commit()
true
}
You dont really need to override onBackPressed() inside the MainActivity but explicitly cast on each fragment and assigning the BottomNavigation like this:
FragmentA.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(activity as MainActivity).main_bottom_navigation.menu.getItem(0).isChecked = true
}
FragmentB.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(activity as MainActivity).main_bottom_navigation.menu.getItem(1).isChecked = true
}
FragmentC.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(activity as MainActivity).main_bottom_navigation.menu.getItem(2).isChecked = true
}
FragmentD.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(activity as MainActivity).main_bottom_navigation.menu.getItem(3).isChecked = true
}
Like this the fragment backstack will properly pop and even exit application when it reaches 0.
This is what I did to handle back press in the activity:
#Override
public void onBackPressed() {
super.onBackPressed();
if(homeFragment.isVisible()){
navView.setSelectedItemId(R.id.nav_home);
}
if(searchFragment.isVisible()){
navView.setSelectedItemId(R.id.nav_search);
}
if(myProfileFragment.isVisible()){
navView.setSelectedItemId(R.id.nav_profile);
}
}
this code is work for me:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
OneSignal.startInit(this)
.inFocusDisplaying(OneSignal.OSInFocusDisplayOption.Notification)
.unsubscribeWhenNotificationsAreDisabled(true)
.init();
bottomNavigationView = findViewById(R.id.bottomNav);
frameLayout = findViewById(R.id.main_frame);
homeFragment = new HomeFragment();
aboutUsFragment = new AboutUsFragment();
recipesFragment = new RecipesFragment();
knowledgeFragment = new KnowledgeFragment();
contactFragment = new ContactFragment();
loadFragment(homeFragment);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.home:
//mManiNav.setItemBackgroundResource(R.color.blue);
loadFragment(homeFragment);
return true;
case R.id.deposit:
// mManiNav.setItemBackgroundResource(R.color.colorAccent);
loadFragment(recipesFragment);
return true;
case R.id.exchange:
//mManiNav.setItemBackgroundResource(R.color.colorPrimary);
loadFragment(knowledgeFragment);
return true;
case R.id.profile:
// mManiNav.setItemBackgroundResource(R.color.light_blue);
loadFragment(aboutUsFragment);
return true;
case R.id.payout:
// mManiNav.setItemBackgroundResource(R.color.light_blue);
loadFragment(contactFragment);
return true;
default:
return false;
}
}
});
}
Here the load fragment class:
private void loadFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.main_frame, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
Here is the popBackStackTillEntry method:
enter code here public void popBackStackTillEntry(int entryIndex) {
if (getSupportFragmentManager() == null) {
return;
}
if (getSupportFragmentManager().getBackStackEntryCount() <= entryIndex) {
return;
}
FragmentManager.BackStackEntry entry = getSupportFragmentManager().getBackStackEntryAt(
entryIndex);
if (entry != null) {
getSupportFragmentManager().popBackStackImmediate(entry.getId(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
Here is the backpress method:
boolean doubleBackToExitPressedOnce = false;
#Override
public void onBackPressed() {
if (doubleBackToExitPressedOnce) {
popBackStackTillEntry(0);
moveTaskToBack(true);
System.exit(0);
return;
}
this.doubleBackToExitPressedOnce = true;
loadFragment(new HomeFragment());
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
doubleBackToExitPressedOnce=false;
}
}, 2000);
}
This is a simple and complete working code in Kotlin.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bottomNavigationView = findViewById(R.id.bottom_navigation)
bottomNavigationView.setOnItemSelectedListener {menuItem ->
when(menuItem.itemId){
R.id.navBottom_menu_1 -> nextFragment(Fragment_1())
R.id.navBottom_menu_2 -> nextFragment(Fragment_2())
R.id.navBottom_menu_3 -> nextFragment(Fragment_3())
R.id.navBottom_menu_4 -> nextFragment(Fragment_4())
else ->false
}
}
}
fun nextFragment(fm:Fragment): Boolean {
supportFragmentManager.beginTransaction().replace(R.id.linearLayout_rootFragment, fm).commit()
return true
}
fun isMenuChecked(itemIndex:Int):Boolean{
return bottomNavigationView.menu.getItem(itemIndex).isChecked
}
fun setMenuItemChecked(itemIndex:Int){
bottomNavigationView.menu.getItem(itemIndex).isChecked = true
}
override fun onBackPressed() {
when(true){
isMenuChecked(3) -> {nextFragment(Fragment_3()) ; setMenuItemChecked(2) }
isMenuChecked(2) -> {nextFragment(Fragment_2()) ; setMenuItemChecked(1) }
isMenuChecked(1) -> {nextFragment(Fragment_1()) ; setMenuItemChecked(0) }
else -> super.onBackPressed()
}
}
}
This is how I solved my,
Wrap your main widget in WillPopScope() and set a function in the onWillpop: as this
Future<bool> _onBackpress() {
if (_currentpage != 0) {
setState(() {
_currentpage--;//decreases number of pages till the fisrt page
});
} else {
// a function to close the app
}
}
The app I am building uses navigation drawer android.support.v4.widget.DrawerLayout and MainActivity extends android.support.v7.app.AppCompatActivity. I have a parent fragment that is replaced by child fragment. On parent fragment default hamburger icon is properly toggling the navigation drawer. While on child fragment, hamburger icon is replaced by back arrow icon using this snippet
((MainActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar = (Toolbar) (getActivity()).findViewById(R.id.toolbar);
((MainActivity)getActivity()).setSupportActionBar(toolbar);
On child fragment back button click event is handled on onOptionsItemSelected() and hamburger icon is shown again for parent fragment on onStop().
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
getActivity().onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
public void onStop() {
super.onStop();
((MainActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false);
MainActivity.toggle.syncState();
}
On parent fragment hamburger icon is now showing properly but on clicking it the navigation drawer doesn't show up again.
Could anyone please help on what am I doing wrong?
Here are the relevant files:
MainActivity.java
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
public static ActionBarDrawerToggle toggle;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
try {
Fragment fragment = (Fragment) ParentFragment.class.newInstance();
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_content, fragment).commit();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.setDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
}
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
}
activity_main.xml
<?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"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
layout="#layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_main_drawer" />
</android.support.v4.widget.DrawerLayout>
ParentFragment.java
public class ParentFragment extends Fragment implements View.OnClickListener {
public static ParentFragment newInstance() {
ParentFragment fragment = new ParentFragment();
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public void onClick(View v) {
int i = v.getId();
Fragment fragment = null;
if(i == R.id.child_fragment_button) {
fragment = new ChildFragment();
}
if(fragment != null) {
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_content, fragment)
.addToBackStack(null)
.commit();
}
}
}
ChildFragment.java
public class ChildFragment extends Fragment {
private Toolbar toolbar;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
// Show back button
((MainActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar = (Toolbar) (getActivity()).findViewById(R.id.toolbar);
((MainActivity)getActivity()).setSupportActionBar(toolbar);
}
#Override
public void onStop() {
super.onStop();
((MainActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false);
MainActivity.toggle.syncState();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
getActivity().onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
I finally came up with the solution. Thanks to this answer.
Here are the changes I made:
ChildFragment.java
public class ChildFragment extends Fragment {
private Toolbar toolbar;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
// Show back button
toggle = MainActivity.toggle;
toggle.setDrawerIndicatorEnabled(false);
toggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentManager manager = getActivity().getSupportFragmentManager();
manager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
toggle.setDrawerIndicatorEnabled(true);
}
});
toolbar = (Toolbar) (getActivity()).findViewById(R.id.toolbar);
toolbar.setNavigationIcon(((MainActivity)getActivity()).getDrawerToggleDelegate().getThemeUpIndicator());
}
#Override
public void onStop() {
super.onStop();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
default:
return super.onOptionsItemSelected(item);
}
}
}
I have two fragments FragmentHome and FragmentAbout, I have added NavigationDrawer to app when I click Home it opens FragmentHome and About opens FragmentAbout, when I open FragmentAbout I am also adding it to backstack. This is working fine.
Now the problem is when I click on About and press back button it goes to the FragmentHome but the NavigationDrawer still shows the About as selected item, I want to change this selected item to Home when I press back button from FragmentAbout
Home Activity:
public class ActivityHome extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
Toolbar toolbar;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
// Toolbar
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Drawer layout
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.nav_drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.nav_drawer_open, R.string.nav_drawer_close);
assert drawer != null;
drawer.setDrawerListener(toggle);
toggle.syncState();
// Navigation view
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
assert navigationView != null;
navigationView.setNavigationItemSelectedListener(this);
// Open first menu item
navigationView.getMenu().performIdentifierAction(R.id.nav_home, 0);
// Set first item checked
navigationView.setCheckedItem(R.id.nav_home);
}
#SuppressWarnings("StatementWithEmptyBody")
#Override
public boolean onNavigationItemSelected(MenuItem item) {
final MenuItem menuItem = item;
// Check if menu item is selected
if (item.isChecked())
item.setChecked(false);
else
item.setChecked(true);
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_home) {
// Open home fragment
getSupportFragmentManager().beginTransaction()
.replace(R.id.frame_layout, new FragmentHome())
.commit();
} else if (id == R.id.nav_about) {
toolbar.setTitle("About");
// Open home fragment
getSupportFragmentManager().beginTransaction()
.replace(R.id.frame_layout, new FragmentAbout())
.addToBackStack(null)
.commit();
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.nav_drawer_layout);
assert drawer != null;
drawer.closeDrawer(GravityCompat.START);
return true;
}
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.nav_drawer_layout);
assert drawer != null;
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}}
FragmentHome
public class FragmentHome extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_home, container, false);
}
#Override
public void onResume() {
super.onResume();
((AppCompatActivity)getActivity()).getSupportActionBar().setTitle(R.string.fragment_home_title);
}
}
FragmentAbout code is same as FragmentHome just layout change.
I have searched a lot on stackoverflow but didn't find any solution yet, so if someone know how to do this please tell me.
There is a OnBackStackChangedListener that you can use for this. Tried it and it works great.
public Fragment getCurrentFragment() {
return this.getSupportFragmentManager().findFragmentById(R.id.fragment_container);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
...
this.getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
Fragment current = getCurrentFragment();
if (current instanceof MyFragmentA) {
navigationView.setCheckedItem(R.id.nav_myfragA);
} else {
navigationView.setCheckedItem(R.id.nav_myfragB);
}
}
});
...
}
This might not be a good solution, but at least it is acceptable for me. You need to store fragment's position inside stack variable (ex: List) whenever a fragment menu is clicked from nav drawer. Then override onBackPressed to remove the last value from stack, and set drawer position to the current last value of stack. Here's the example:
private List<Integer> itemPositionStacks = new ArrayList<>();
protected void onCreate(#Nullable Bundle savedInstanceState) {
itemPositionStacks.add(currentSelectedPosition);
}
public void onNavigationViewItemSelected(int itemId) {
Fragment fr = null;
switch (itemId) {
case 0:
fr = new aFragment();
break;
case 1:
fr = new bFragment();
break;
}
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, fr)
.addToBackStack(null)
.commit();
Drawer.getInstance().setDrawerChecked(itemId);
itemPositionStacks.add(itemId);
}
public void onBackPressed() {
if(itemPositionStacks.size() > 1) {
itemPositionStacks.remove(itemPositionStacks.size() - 1);
Drawer.getInstance().setDrawerChecked(itemIdStacks.get(itemIdStacks.size() - 1));
}
super.onBackPressed();
}
In your onBackPressed() function ..
you must choose which item to be selected by using
drawer.setCheckedItem(R.id.nav_home);
and remove the selection of the another item
drawer.getMenu().findItem(R.id.nav_about).setChecked(false);