My app has a navigation drawer with three items linking to three fragments A, B and C
when I click on the back button from any of the Fragments, the app exits.I solved this by declaring these constants in my base activity:
public static boolean IS_FRAGMENT_A = false;
public static boolean IS_FRAGMENT_B = false;
public static boolean IS_FRAGMENT C = false;
and then in the selectView method I did this:
private void displayView(int position) {
IS_FRAGMENT_A = false;
IS_FRAGMENT_B = false;
IS_FRAGMENT_C = false;
// update the main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
fragment = new FragmentA();
IS_FRAGMENT_A = true;
break;
case 1:
fragment = new FragmentB();
IS_FRAGMENT_B = true;
break;
case 2:
fragment = new FragmentC();
IS_FRAGMENT_C = true;
break;
default:
break;
}
And in the overriden onBackPressed() method I did this
public void onBackPressed() {
if (IS_FRAGMENT_A) {
finish();
} else {
displayView(0);
}
}
This works fine when am navigating to these fragments from the navigation drawer.However, when I go to any of the fragments through an ActionBar button, the onBackPressed method does not work.
In Fragment A, I have an ActionBar button that takes me to Fragment B:
public boolean onOptionsItemSelected(MenuItem menuItem) {
int id = menuItem.getItemId();
switch (id) {
case R.id.action_new:
Fragment fragmentB = new FragmentB();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frame_container, fragmentB);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
return true;
default:
return super.onOptionsItemSelected(menuItem);
}
}
When I press the back button from FragmentB after navigating to it via this method the app exits, unlike when I navigate to it through the navigation drawer.
I have tried adding this to the onCreate() method of FragmentB with no luck:
BaseActivity.IS_FRAGMENT_B = true;
My question is, how do I make the back button work in all fragments irrespective of how I got there?
Related
I have three fragment in NavigationBarView.
First fragment has gridview(gridview data is dynamic by network search).
But when I go to second fragment and back to first fragment, the gridview in first fragment is clear.
How can I prevent the gridview's data from being cleared?
Here my navigation code.
// in global variable
NavigationBarView navigationBarView;
FirstFragment firstFragment;
SecondFragment secondFragment;
ThirdFragment thirdFragment;
// ...
firstFragment = new FirstFragment();
secondFragment= new SecondFragment();
thirdFragment = new ThirdFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.containers, firstFragment).commit();
navigationBarView = findViewById(R.id.bottom_navigationview);
navigationBarView.setKeepScreenOn(true);
navigationBarView.setOnItemSelectedListener(new NavigationBarView.OnItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch(item.getItemId())
{
case R.id.menu_first:
getSupportFragmentManager().beginTransaction().replace(R.id.containers, firstFragment).commit();
return true;
case R.id.menu_second:
getSupportFragmentManager().beginTransaction().replace(R.id.containers, secondFragment).commit();
return true;
case R.id.menu_third:
getSupportFragmentManager().beginTransaction().replace(R.id.containers, thirdFragment).commit();
return true;
}
return false;
}
});
addToBackStack method when replacing one fragment by another:
getFragmentManager().beginTransaction().replace(R.id.content_frame, fragment).addToBackStack("my_fragment").commit();
and also use onbackpressed to back with state using back button
#Override
public void onBackPressed() {
if (getParentFragmentManager().getBackStackEntryCount() > 0) {
getParentFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
use same popBackStack() to navigate from navigationbar also
I am using a navigation bottom with 4 items in my app, so I have 4 fragments.
the first fragment(home page) contains a recyclerView and other fragments don't contain any recyclerView.
The problem is here;
when I navigate to other fragments I can see the recycler view in the background.
and when I navigatie back to the first fragment there is another recycler view under the original one!
I have used this :
fm.beginTransaction().hide(active).show(fragment2).commit();
but the hide() method doesn't work.
Here is the related parts of my code:
I have globally defined these
final Fragment fragment1 = new HomeFragment();
final Fragment fragment2 = new AddFragment();
final Fragment fragment3 = new CalendarFragment();
final Fragment fragment4 = new ProfileFragment();
final FragmentManager fm = getSupportFragmentManager();
Fragment active = fragment1;
then
In the onCreate :
fm.beginTransaction().add(R.id.nav_host_fragment, fragment4, "4").hide(fragment4).commit();
fm.beginTransaction().add(R.id.nav_host_fragment, fragment3, "3").hide(fragment3).commit();
fm.beginTransaction().add(R.id.nav_host_fragment, fragment2, "2").hide(fragment2).commit();
fm.beginTransaction().add(R.id.nav_host_fragment, fragment1, "1").commit();
and at last
the navigation item listener :
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
if (active == fragment1)
return false;
fm.beginTransaction().hide(active).show(fragment1).commit();
active = fragment1;
return true;
case R.id.navigation_add:
if (active == fragment2)
return false;
fm.beginTransaction().hide(active).show(fragment2).commit();
active = fragment2;
return true;
case R.id.navigation_calendar:
if (active == fragment3)
return false;
fm.beginTransaction().hide(active).show(fragment3).commit();
active = fragment3;
return true;
case R.id.navigation_profile:
if (active == fragment4)
return false;
fm.beginTransaction().hide(active).show(fragment4).commit();
active = fragment4;
return true;
}
return false;
}
};
I have used navGraph in my fragment in the XML file previously and I've forgotten to delete the navGraph, so it was showing the first fragment in the navGraph in the background.
I have created an app with a bottom navigation fragment which consists of 5 main fragments and one extra sub fragment (login Fragment). the thing is I want this login fragment to be replaced with the userAccount fragment once user has logged in successfully.
Note: I am running the App statically at first so I am using a variable Boolean Called Status to check whether user has logged in or not
private static final boolean Status = false;
final Fragment f1 = new HomeFragment();
final Fragment f2 = new SearchFragment();
final Fragment f3 = new CameraFragment();
final Fragment f4 = new ChatFragment();
final Fragment f5 = new AccountFragment();
// logginFragment page should be replaced with AccountFragment once user logged in successfully
final Fragment f6 = new logginFragment();
private BottomNavigationViewEx.OnNavigationItemSelectedListener navListener =
new BottomNavigationViewEx.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.nav_home:
fm.beginTransaction().hide(active).show(f1).commit();
active = f1;
return true;
case R.id.nav_search:
fm.beginTransaction().hide(active).show(f2).commit();
active = f2;
return true;
case R.id.nav_camera:
fm.beginTransaction().hide(active).show(f3).commit();
active = f3;
return true;
case R.id.nav_chat:
fm.beginTransaction().hide(active).show(f4).commit();
active = f4;
return true;
case R.id.nav_account:
fm.beginTransaction().hide(active).show(f5).commit();
active = f5;
return true;
}
/* \getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
selectedFragment).commit();*/
return false;
}
};
Your Code changes the fragment only when one of the Bottom Navigation Bar item is selected. You can replace the login fragment with account fragment when the login is successful(i.e when button is tapped) and set it to active rather than listening for the change in selection. Let me know if this helps.
void login()
{
if(Successful)
{
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_login,R.id.fragment_account)
.commit();
}
}
I have the following bottom navbar code to switch between 3 fragments:
public class MainActivity extends AppCompatActivity {
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.navigation_home:
fragment = new HomeFragment();
break;
case R.id.navigation_dashboard:
fragment = new DashboardFragment();
break;
case R.id.navigation_notifications:
fragment = new NotificationsFragment();
break;
}
return loadFragment(fragment);
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadFragment(new HomeFragment());
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
}
private boolean loadFragment(Fragment fragment) {
//switching fragment
if (fragment != null) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit();
return true;
}
return false;
}
}
In the fragments there are RecyclerViews with lists. Every time I switch between the tabs (between fragments), it looks like the fragment is reloaded, and the lists jump to the top. I want to prevent that reloading so that the user stays on the same place in the list he viewed before switching fragments
The problem is that you are creating a new instance every time. You can cache the instance like:
private Fragment mHomeFragment = new HomeFragment();
private Fragment mDashboardFragment = new DashboardFragment();
private Fragment mNotificationsFragment = new NotificationsFragment();
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.navigation_home:
fragment = mHomeFragment;
break;
case R.id.navigation_dashboard:
fragment = mDashboardFragment;
break;
case R.id.navigation_notifications:
fragment = mNotificationsFragment;
break;
}
return loadFragment(fragment);
}
As we could see, you are always replace your fragment when clicks on bottom navigation, replace means previous fragment removes and state cleans. The solution is do not create your fragment each time and use attach/detach method for showing actual fragment. Here is already described about these methods.
I have a bottom navigation bar that contains 4 fragments and when a tab is selected a new instance of that fragment is loaded.
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.navigation_home:
fragment = HomeFragment.newInstance();
break;
case R.id.navigation_cards:
fragment = CardsFragment.newInstance();
break;
case R.id.navigation_deals:
fragment = DealsFragment.newInstance();
break;
case R.id.navigation_settings:
fragment = SettingsFragment.newInstance();
break;
}
if (fragment != null) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.content, fragment);
fragmentTransaction.commit();
}
return true;
}
};
Now in my HomeFragment there is a RecyclerView that On Item Select it opens a new Fragment:
myViewHolder.cardView.setOnClickListener(view -> {
System.out.println("clicked");
Fragment fragment = new TargetDetailsFragment();
FragmentTransaction ft = ((AppCompatActivity) context).getSupportFragmentManager()
.beginTransaction();
ft.replace(R.id.content, fragment).addToBackStack(null);
ft.commit();
});
I want to add a back button to the TargetDetails Fragments that takes you back to the home page when selected and I attempted doing that by implementing OnBackStackChangedListener in the Main activity
#Override
public void onBackStackChanged() {
shouldDisplayHomeUp();
}
public void shouldDisplayHomeUp(){
//Enable Up button only if there are entries in the back stack
boolean canback = getSupportFragmentManager().getBackStackEntryCount()>0;
getSupportActionBar().setDisplayHomeAsUpEnabled(canback);
}
#Override
public boolean onSupportNavigateUp() {
//This method is called when the up button is pressed. Just the pop back stack.
getSupportFragmentManager().popBackStack();
return true;
}
}
but the problem is when I click on it its reloads the HomeFragment Again but I simply want it to go back to the saved instance of that Fragment
Here is my code that I have used for the ViewPager, the idea is to see if the current page number is 0 than to proceed super.onBackPressed(); otherwise go to the previous fragment:
#Override
public void onBackPressed() {
if(vpPager.getCurrentItem()!=0) {
vpPager.setCurrentItem(vpPager.getCurrentItem()-1, true);
} else {
super.onBackPressed();
}
}
By adding below code in your Activity.
The fragment back stack can be managed.
#Override
public void onBackPressed() {
int count = getFragmentManager().getBackStackEntryCount();
if (count == 0) {
super.onBackPressed();
//additional code
} else {
getFragmentManager().popBackStack();
}
}
Hello #Bolu Okunaiya i think you should try this it will help you to manage backstack to desired fragment without loading same fragment again.
For Stop loading previous fragment you should use "add" instead of "replace" with your FragmentTransaction
Inside your MainActivity
#Override
public void onBackStackChanged() {
//shouldDisplayHomeUp();
Fragment currentFragment = getActivity().getFragmentManager()
.findFragmentById(R.id.fragment_container);
if (currentFragment instanceof TargetDetails) {
Log.v(TAG, "your current fragment is TargetDetails");
popBackStackImmediate(); //<<<< immediate parent fragment will open,which is your HomeFragement
}
}
To use popBackStackImmediate() you need to *replace FragmentA with FragmentB and use addToBackstack() before commit().