I have a NavigationView that changes certain Fragment by clicking on its item and setting toolbar's title with item's name:
fragmentTransaction = getFragmentManager().beginTransaction();
int id = item.getItemId();
switch (id){
case R.id.studying:
fragmentTransaction.replace(R.id.container, studyFragment).addToBackStack(null);
break;
case R.id.events:
fragmentTransaction.replace(R.id.container, eventsFragment).addToBackStack(null);
break;
case R.id.discount:
fragmentTransaction.replace(R.id.container, discountsFragment).addToBackStack(null);
break;
case R.id.bookmarks:
fragmentTransaction.replace(R.id.container, bookmarksFragment).addToBackStack(null);
break;
case R.id.settings:
fragmentTransaction.replace(R.id.container, settingsFragment).addToBackStack(null);
break;
case R.id.prolong_sign:
fragmentTransaction.replace(R.id.container, prolongFragment).addToBackStack(null);
break;
case R.id.contacts:
fragmentTransaction.replace(R.id.container, contactsFragment).addToBackStack(null);
break;
}
toolbarTitle.setText(item.getTitle().toString());
fragmentTransaction.commit();
Proving better usability I override onBackPressed method:
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
FragmentManager fm = getFragmentManager();
if (fm.getBackStackEntryCount() > 0 && fm.getBackStackEntryCount() != 1) {
fm.popBackStackImmediate();
} else if (fm.getBackStackEntryCount() == 1) {
toolbarTitle.setText("TEST");
fm.popBackStackImmediate();
} else {
super.onBackPressed();
}
}
}
If I back to the first Fragment, my toolbarTitle sets okay according to:
else if (fm.getBackStackEntryCount() == 1) {
toolbarTitle.setText("TEST");
fm.popBackStackImmediate();
}
But I don't know how to make toolbarTitle to change title correctly on each onBackPressed and make previous item in NavigationView checked. Can you help me with it?
EDIT
I created an interface
public interface OnToolbarTitle {
void setTitle(String title);
}
implemented it MainActivity and overrided
#Override
public void setTitle(String title) {
toolbarTitle.setText(title);
}
And tried to use it in Fragments by attaching it in Fragments:
#Override
public void onAttach(Context context) {
super.onAttach(context);
titleSetter = (OnToolbarTitle) context;
}
#Override
public void onResume() {
super.onResume();
titleSetter.setTitle("Test-frag3");
}
But I got NullPointerException in line with setTitle() method.
EDIT
Ok I solved my problem by accessing Toolbar in fragment.
View view = getActivity().findViewById(R.id.toolbar);
TextView title = (TextView) view.findViewById(R.id.toolbar_title);
title.setText("study lol");
Still, I'd be glad to see, how can I achive it with interface.
You should consider making your fragments setting the title. I.e. define interface like:
interface HostContract {
setTitle(String title);
}
and make your activity implement it. Then in each fragment you should call ((HostContract)getActivity).setTitle() in fragment's onResume(). This way you set the title when your fragment goes into game (be it because it is newly added, or because you took previous one from backstack, which should solve your problem. And that's basically all you need to do.
your if...else should be like
if (fm.getBackStackEntryCount() > 0 && fm.getBackStackEntryCount() != 1) {
fm.popBackStackImmediate();
toolbarTitle.setText("TEST");
} else if (fm.getBackStackEntryCount() == 1) {
toolbarTitle.setText("TEST");
fm.popBackStackImmediate();
} else {
super.onBackPressed();
}
Related
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().
I've handled fragments before but that was with a view pager and tablayout and I was able to keep the fragments state by building an array and returning a frag from that array.
but now using fragment manager, I have access to add and replace, but both create new instances of my fragments without saving what has occured (i.e. typing on editText).
How can I keep or reuse the fragments I've created when navigating from the menu (keep in mind some of these fragments will have a child backstack, the back button should only take you out of deep fragments not my main ones)
current code
private void initNavigation(){
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
/* Initialize fragments for navigation drawer */
fragments = new Fragment[]{
new ProfileFragment(),
new StatsFragment()};
/* Set selection of navigation item */
nav_view = (NavigationView) findViewById(R.id.nav_view);
nav_view.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
item.setChecked(true);
FragmentManager fragmentManager = getSupportFragmentManager();
switch (item.getItemId()) {
case R.id.nav_routine:
if(fragmentManager.findFragmentByTag("TAG0") != null){
Log.i("fm", "reloading profile frag");
fragmentManager.beginTransaction().replace(R.id.content_main,
fragmentManager.findFragmentByTag("TAG0"), "TAG0");
} else{
fragmentManager.beginTransaction().replace(R.id.content_main, fragments[0], "TAG0").commit();
}
break;
default:
if(fragmentManager.findFragmentByTag("TAG1") != null){
Log.i("fm", "reloading stats frag");
fragmentManager.beginTransaction().replace(R.id.content_main,
fragmentManager.findFragmentByTag("TAG1"), "TAG1");
} else{
fragmentManager.beginTransaction().replace(R.id.content_main, fragments[1], "TAG1").commit();
}
break;
}
drawerLayout.closeDrawers();
return true;
}
}
);
}
Edit solved:
I found out that in order for a view to be saved it must have an ID (makes sense). Also I wasn't using .commit() with some of my .replace in my prior code
nav_view.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
item.setChecked(true);
FragmentManager fragmentManager = getSupportFragmentManager();
switch (item.getItemId()) {
case R.id.nav_routine:
fragmentManager.beginTransaction().replace(R.id.content_main, fragments[0], "TAG0").commit();
break;
default:
fragmentManager.beginTransaction().replace(R.id.content_main, fragments[1], "TAG1").commit();
break;
}
drawerLayout.closeDrawers();
return true;
}
}
);
Im not clear with your question .But this is how keeping backState of fragments.
Fragment mfragment=new Fragmentname();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.fragment_container, mfragment);
String backStateName = mfragment.getClass().getName();
if (position != 0) {
ft.addToBackStack(backStateName);
}
ft.commit();
As you could save the data of a Fragment when leaving from it I think you already know about SavedInstanceState. Anyway, while you're transacting one fragment to another from your Activity using fragment transaction, you need to add addToBackStack(null) before your commit. So the transaction should look like
fragmentManager.beginTransaction().replace(R.id.content_main, fragments[0], "TAG0").addToBackStack(null).commit();
And override the onBackPressed function of your Activity like this.
#Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0)
getSupportFragmentManager().popBackStack();
else
super.onBackPressed();
}
I have created a tabbed activity inside a navigation drawer activity. I have linked the lists inside the navigation drawer with individual fragments. I also used the command ft.addToBackStack(null) before ft.commit(). It was perfectly fine and I was able to use it to close the fragments inside the navigation drawer. now I have created webviews inside the fragments of the tabbed activity (home page). I added the onBackPressed to go back inside the webview and now the addToBackStack method stopped working.
For the navigation drawer fragments,
private void dispaySelectedScreen(int id) {
Fragment fragment = null;
switch (id) {
case R.id.fragment1:
fragment = new Fragment1();
break;
case R.id.fragment2:
fragment = new Fragment2();
break;
case R.id.fragment3:
fragment = new Fragment3();
break;
case R.id.fragment4:
fragment = new fragment4();
break;
}
if (fragment != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_main, fragment);
ft.addToBackStack(null);
ft.commit();
}
onBackPressed Method
#Override
public void onBackPressed() {
if (hWebView.canGoBack()) {
hWebView.goBack();
} else if (!getFragmentManager().popBackStackImmediate())
if (sWebView.canGoBack()) {
sWebView.goBack();
} else if (!getFragmentManager().popBackStackImmediate())
if (dWebView.canGoBack()) {
dWebView.goBack();
} else if (!getFragmentManager().popBackStackImmediate());
}
Please help me out!!!
try this code: override onBackPressed() in your tabbed activity (home page).
#Override
public void onBackPressed() {
FragmentManager fm = getSupportFragmentManager();
if (hWebView.canGoBack()) {
hWebView.goBack();
} else if (!getFragmentManager().popBackStackImmediate())
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
} else {
super.onBackPressed();
}
}
I am trying to close the fragment and wants to resume the main activity by using the addToBackStack in my fragment implimentation but it is not working. Back button is closing the application.
The fragment implimentation method that I am using is,
private void dispaySelectedScreen(int id) {
Fragment fragment = null;
switch (id) {
case R.id.facebook_login:
fragment = new FacebookLogin();
break;
case R.id.memes:
fragment = new Memes();
break;
case R.id.submit_image:
fragment = new SubmitImage();
break;
case R.id.discussion:
fragment = new Discussions();
break;
case R.id.invite:
fragment = new Invite();
break;
case R.id.connect_fb:
fragment = new FacebookConnect();
break;
case R.id.connect_twitter:
fragment = new TwitterConnect();
break;
case R.id.connect_instagram:
fragment = new InstaConnect();
break;
}
if (fragment != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_main, fragment);
ft.addToBackStack(null);
ft.commit();
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
I have also declared one onBackPressed method for the drawer,
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
Please suggest..
fragment.addToBackStack(null) is enough to open the previous activity but if you want to handle some action on back pressed apart from removing the fragment then we override onBackPressed(), so this is my tested approach, kindly have a look:-
Below is my implementation:-
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_holder, new MyFragment(), "MY_FRAG")
.addToBackStack(null).commit();
}
#Override
public void onBackPressed() {
MyFragment fragment = (MyFragment) getSupportFragmentManager().findFragmentByTag("MY_FRAG");
if (fragment != null) {
if (fragment.isVisible()) {
//todo perform any action if you want when fragment is removed
}
} else {
super.onBackPressed();
}
}
I see you are using replace on transaction in that way your fragment will pop up from stack but it will reload i.e onCreateView() of that fragment will be called . Just replace
ft.replace(R.id.content_main, fragment);
to
ft.add(R.id.content_main, fragment);
For handling back button use following code .
public void onBackPressed() {
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
//that means your stack is empty and you want to close activity
finish();
} else {
// pop the backstack here
getSupportFragmentManager().popBackStackImmediate();
}
}
}
Navigation drawer with fragments. I have placed one fragment on main activity. But if i back press the same fragment comes again. How can I handle this??
and secondly how to implement onBackpressed in fragments??
Can anybody help me out?
in onCreate of main activity I have
FragmentTransaction tx = getFragmentManager().beginTransaction();
tx.replace(R.id.mainFrame, new PlaceOrderFragment());
tx.commit();
and in back press of it I have
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
First you need to add the fragmnet to back stack so we could retrieve it later in onBackPressed()
Replace your fragmnet transaction with this
FragmentTransaction tx = getFragmentManager().beginTransaction();
tx.replace(R.id.mainFrame, new PlaceOrderFragment());
// HERE WE WILL ADD FRAGMENT TO BACKSTACK SO WE COULD GET BACK TO IT
tx.addToBackStack(null);
tx.commit();
Replace your onBackPressed with this
#Override
public void onBackPressed() {
// CHECKS HOW MANY FRAGMENTS ARE IN THE BACKSTACK
int count = getFragmentManager().getBackStackEntryCount();
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
if (count == 0) {
// IF NO FRAGMENT IN THE BACKSTACK CALL
super.onBackPressed();
} else {
// GET THE LAST OPENED FRAGMENT
getFragmentManager().popBackStack();
}
}
}
You can do this using Interface. That interface will implemented in each fragment. Here is a simple example:
public interface OnBackPressedListener {
void onBackPressed();
}
Here, if you implement this interface in your fragment then you will found this method:
#Override
public void onBackPressed() {
getActivity().finish(); // Or Do your specific task
}
Now your Activity that holds that fragment , that should use onBackPressed() callback. Write bellow code in that method
#Override
public void onBackPressed() {
if (YOUR_DRAWER.isDrawerOpen(GravityCompat.START)) {
YOUR_DRAWER.closeDrawer(Gravity.LEFT); //CLOSE Nav Drawer!
return;
}
Fragment fragment=getYourVisibleFragment();
if(fragment!=null){
((OnBackPressedListener)fragment).onBackPressed();
}else{
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
If you want to get Visible fragment then just use this method
public <F extends Fragment> F getVisibleFragment() {
List<Fragment> fragments = getSupportFragmentManager().getFragments();
if (fragments != null) {
for (Fragment fragment : fragments) {
if (fragment != null && fragment.isVisible()) {
return (F) fragment;
}
}
}
return null;
}
That's it :)
FragmentTransaction tx = getFragmentManager().beginTransaction();
tx.replace(R.id.mainFrame, new PlaceOrderFragment()).addToBackStack("your tag");
tx.commit();
Override onBackPress() and try following code.
#Override
public void onBackPressed() {
int count = getFragmentManager().getBackStackEntryCount();
if (count == 0) {
super.onBackPressed();
//your other utility code if needed
} else {
getFragmentManager().popBackStack();
}
}
Hope this will work for you.
Replace ypu fragment in onCreate with fragment id , and then implement onBackpressed on the same activity and find that fragment with id and check is it visible or null and then perform operations based on it.
hope you got this.