I have a main activity in which I designate tabs using three fragments. I have a button on the ActionBar which navigates to a different fragment say "Info about the app" Once user navigates to this particular fragment (Info) I disable it so that it is not called again and again. Then on the back key in the main activity I re-enable it. So far so good. But I am not able to re-enable it for one scenario: Say if user navigates to the info fragment and does not press back, but however if he navigates to a different tab, the info button is still disabled because back-press has not been called. I tried a lot of things in onStart() and onResume() of fragments but I am not able to reference the menuItem in any of those as I get a null pointer.
Code Reference: (MainActivity while calling the info fragment from onOptionsSelected):
public boolean onOptionsItemSelected(MenuItem item) {
mMenuItem = item;
switch (item.getItemId()) {
case R.id.info:
Tab d = getActionBar().getSelectedTab();
System.out.println(""+d.getText().toString());
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
String a = d.getText().toString();
if(a.equalsIgnoreCase("Reminders")){
FragmentContact fragmentcontact = new FragmentContact();
fragmentTransaction.replace(R.id.realtabcontent, fragmentcontact);
mMenuItem.setEnabled(false);
//mMenuItem.setIcon(R.drawable.btn_age_01);
}
else if(a.equalsIgnoreCase("Notifications")){
FragmentContact fragmentcontact = new FragmentContact();
fragmentTransaction.replace(R.id.realtabcontent2, fragmentcontact);
mMenuItem.setEnabled(false);
}
else if(a.equalsIgnoreCase("Contacts")){
FragmentContact fragmentcontact = new FragmentContact();
fragmentTransaction.replace(R.id.realtabcontent3, fragmentcontact);
mMenuItem.setEnabled(false);
}
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
break;
on Back key(Main Activity):
#Override
public void onBackPressed() {
mMenuItem.setEnabled(true);
super.onBackPressed();
}
The solution was very simple, to set an options menu for individual "Fragments" use:
setHasOptionsMenu(true);
Cheers!!
Related
I have a simple BottomNavigationView with two menu items (Home Fragment, Settings Fragment) in an activity.
I have implemented onNavigationItemSelectedListener and onNavigationItemSelected.
Also bottomNavigationView.setOnNavigationItemSelectedListener(this);
App page lands on the Home Fragment.
onNavigationItemSelected is being called when I switch between menu items but When I first launch the app and tap on the same menu Item i.e. Home Fragment, onNavigationItemSelected is not being called.
I would need to show a toast whenever the user clicks on the home page when user is already in home page but onNavigationItemSelected event is not triggered.
As Mike M mentioned,
setOnNavigationItemReselectedListener did the trick.
First of all we do this in the MainActivity
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
int v = item.getItemId();
if(v==R.id.home)
{
getSupportActionBar().setTitle("Home");
Fragment fragment = new HomeFragment();
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction().replace(R.id.frame_layout, fragment).commit();
}
else if (v==R.id.dash_board)
{
getSupportActionBar().setTitle("Dashboard");
Fragment fragment = new DashboardFragment();
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction().replace(R.id.frame_layout, fragment).commit();
}
return true;
}
};
This is a example here
If you want to know in detail then click here
I have Base Activity including NavigationView with 2 menu items. On start it loads Home fragment having background image inside it. Each loads specific fragment. When I select Terms & Conditions menu item, it loads T&C fragment & when I press back button it simply kills it.
However, when I select About Us menu item, it loads About Us fragment but I need to press BACK button twice to kill it. I need to know why does it happen?
Part of Code in AppBaseActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
fragmentManager = getSupportFragmentManager();
fragmentTransaction = fragmentManager.beginTransaction();
HomeFragment homeFragment = new HomeFragment();
fragmentTransaction.add(R.id.body_container, homeFragment, "");
fragmentTransaction.commit();
}
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
navigationView.getMenu().findItem(item.getItemId()).setChecked(true);
switch (item.getItemId()) {
case R.id.nav_terms :
fragmentTransaction = fragmentManager.beginTransaction();
TCFragment tcFragment = new TCFragment();
fragmentTransaction.replace(R.id.body_container, tcFragment, "");
fragmentTransaction.commit();
break;
case R.id.nav_about_us :
fragmentTransaction = fragmentManager.beginTransaction();
AboutUsFragment aboutUsFragment = new AboutUsFragment();
fragmentTransaction.replace(R.id.body_container, aboutUsFragment, "");
fragmentTransaction.commit();
break;
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
All fragments simply have overridden onCreateView() by inflating respected xml only. No code is written in both fragments yet.
You can stop back hardware navigation if you want.
Simply using onBackPressed() without super.onBackPressed()
#Override
public void onBackPressed() {
}
#Override
public void onBackPressed() {
super.onBackPressed();
}
I added a menu with some actions, which are handled in MainActivity as well. For example after firing the 'Logout' menu action, MainActivity method 'onOptionsMenuSelected()' want to forward to LoginFragment. Unfortunately the text elements of the former fragments are still showing up, altough I am calling replace() of FragmentTransaction to change the relevant fragment to show in my forwardToLoginFragment() method. What needs to be done to hide old fragments and their content successfully?
Moreover, sometimes my app stops, because FragmentManager tries to add a fragment to the activity more than once. This results in an app crash. How to manage the fragments wisely to prevent this issue as well?
Excerpts of my MainActivity class:
private void forwardToLoginFragment(){
LoginFragment loginFragment = (LoginFragment) getFragmentManager().findFragmentByTag(LoginFragment.LOGIN_FRAGMENT_TAG);
FragmentTransaction ft = getFragmentManager().beginTransaction();
if (loginFragment == null){
loginFragment = new LoginFragment();
ft.add(R.id.container, loginFragment, LoginFragment.LOGIN_FRAGMENT_TAG);
} else {
ft.replace(R.id.container, loginFragment);
}
ft.commit();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
...
case R.id.menu_main_signout:
Toast.makeText(this, "Signing out ...", Toast.LENGTH_SHORT).show();
setSignedIn(false);
forwardToLoginFragment();
return true;
...
default:
return super.onOptionsItemSelected(item);
}
}
None of the other questions I have read on stackoverflow have been able to help with my problem. As far as I can tell, I am doing everything correctly.
I have a master/detail flow with fragments.
Upon creation of the main activity, the master fragment is loaded with the following code:
Fragment frag;
frag = new MainListFragment();//<-- **the master fragment**
FragmentManager fm = getFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
transaction.replace(R.id.fragment_container, frag);
Log.d("My Debug Bitches", "stack:" + fm.getBackStackEntryCount());
transaction.commit();
The master fragment has a ListView; clicking on a list item brings up the details fragment like so:
#Override
public void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
FragmentManager fm = getFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
SubListFragment frag = new SubListFragment();//<-- **the detail fragment**
transaction.replace(R.id.fragment_container, frag);
transaction.addToBackStack(null);
transaction.commit();
fm.executePendingTransactions();
Log.d("My Debug Bitches", "stack:" + fm.getBackStackEntryCount());
}
Now, according to LogCat, the BackStackEntryCount changes from 0 to 1 after I navigate from master fragment to detail fragment:
So why is it that, when I click the back button while in the details fragment, that the app closes instead of returning to the master fragment??????????
You have to add the popBackStack() call to the onBackPressed() method of the activity.
Ex:
#Override
public void onBackPressed() {
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStack();
} else {
super.onBackPressed();
}
}
#Bobbake4's answer is awesome, but there is one little problem.
Let's say I have three fragments A, B and C.
A is the main Fragment (the fragment that shows when I launch my app), B and C are fragments I can navigate to from the navigation drawer or from A.
Now, when I use the back button from B or C, I go back to the previous fragment (A) alright, but the title of the previous fragment (fragment B or C) now shows in the actionBar title of Fragment A. I have to press the back button again to "truly" complete the back navigation (to display the view and correct title for the fragment and returning to)
This is how I solved this problem. Declare these variables.
public static boolean IS_FRAG_A_SHOWN = false;
public static boolean IS_FRAG_B_SHOWN = false;
public static boolean IS_FRAG_C_SHOWN = false;
In the MainActivity of my app where am handling navigation drawer methods, I have a method displayView(position) which handles switching of my fragments.
private void displayView(int position) {
IS_FRAG_A_SHOWN = false;
IS_FRAG_B_SHOWN = false;
IS_FRAG_C_SHOWN = false;
// update the main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
fragment = new FragmentA();
IS_FRAG_A_SHOWN = true;
break;
case 1:
fragment = new FragmentB();
IS_FRAG_B_SHOWN = true;
break;
case 2:
fragment = new FragmentC();
IS_FRAG_C_SHOWN = true;
break;
default:
break;
}
finally, in my onBackPressed method, I do this:
public void onBackPressed() {
if(fragmentManager.getBackStackEntryCount() != 0) {
fragmentManager.popBackStack();
if (IS_FRAG_A_SHOWN) { //If we are in fragment A when we press the back button, finish is called to exit
finish();
} else {
displayView(0); //else, switch to fragment A
}
} else {
super.onBackPressed();
}
}
I have a simple app that has two fragments and when in landscape mode, both fragments are shown side by side and in portrait I show Fragment A and then if they select an option, start an Activity that shows Fragment B. My problem is when I am in Portrait mode and showing Fragment B, if the user selects a menu option I want to refresh or redraw the View that is associated with Fragment B and can’t understand how to make this work. I tried the getView method and getLayoutInflater method but the screen doesn’t change because I think I am creating a new view. I also tried to get a reference to Fragment A thinking I could call its routine to build a new fragment and replace Fragment B but can’t get a reference to it because it is not being displayed. What I really need to do is just force the onCreateView method to be called again to inflate the new view but I have tried several methods to try to do this but can’t get the onCreateView to be called again. Any thoughts on how to this?
You will want to perform a FragmentTransaction and use the replace() method
This shouldn't be too hard to do, but the answer will depend on where your Menu is located (i.e. is your onOptionsItemSelected() call back inside your parent Activity or is it in either Fragment A/B?).
Suppose for simplicity's sake, your menu implementation and onOptionsItemSelected() is in the parent activity, and you want to reshape the fragments in the event that menu_option1 is chosen. It would look something like this:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
//...
switch (item.getItemId()) {
case R.id.menu_option1:
//do something
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment newFragment = new YourFragmentClass();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.your_fragment_id, newFragment);
transaction.addToBackStack(null);
transaction.commit();
return true;
case R.id.menu_option2:
//do something else;
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Alternatively, if your menu is a child of one of your Fragments (which it should be for the sake of more reusable code), then one method is to require that host Activity to implement an interface defined by the Fragment, that can be used as a call back.
And in the onOptionsItemSelected() callback inside your fragment class, you simply make a call to this callback method.
Although it sounds like a mouthful, you only really need to do a couple of things. Pretend that this is your Fragment class
public static class FragmentA extends ListFragment {
OnSelectedListener mListener;
// Container Activity must implement this interface
public interface OnSelectedListener {
public void onSelected();
}
...
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
//This is to ensure that the Activity has implemented the interface we set up above
try {
mListener = (OnSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnSelectedListener");
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
//...
switch (item.getItemId()) {
case R.id.menu_option1:
//do something
getActivity().onSelected();
return true;
case R.id.menu_option2:
//do something else;
return true;
default:
return super.onOptionsItemSelected(item);
}
}
...
}
Then in the Activity, you would see something like:
public class MainActivity extends Activity implements FragmentA.onSelectedListener{
...
public void onSelected(){
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment newFragment = new YourFragmentClass();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.your_fragment_id, newFragment);
transaction.addToBackStack(null);
transaction.commit();
}
}
if the user selects a menu option I want to refresh or redraw the View that is associated with Fragment B and can’t understand how to make this work
In onOptionsItemSelected(), have the activity call a method on the fragment that causes it to update its widgets with the new content. Or, have the activity execute a FragmentTransaction to replace the fragment (if the fragment was originally set up via a FragmentTransaction).