Clear Toolbar menu options added by fragment when it is replaced
Asked Answered
T

3

11

I have a set up of 3 'top-level' fragments that each use their own Child Fragment Managers to offer drill-down navigation. These top-level fragments are switched around by being replaced using the main activity's Support Fragment Manager.

One of this top-level fragments has a child fragment that adds a menu option to the toolbar/actionbar using setHasOptionsMenu() and onCreateOptionsMenu() and that works just fine.

Now the issue I just noticed is this:

When a new child fragment is added and the child fragment with the menu item is hidden (and the transaction added to the backstack of the top-level fragment), the menu item goes away. Similarly, when the fragment is visible again by undoing the transaction, the menu item comes back. This is desired behavior and seems to be handled entirely by the Fragment framework.

HOWEVER, if the child fragment is visible (and thus, its menu item is present in the toolbar) and I switch top-level fragments, the menu item remains in the toolbar.

I would have expected the menu item to be cleared as not only the child fragment it belongs to has been dumped, but even its parent fragment (on of the top-level fragments) has also been completely replaced (not even added to the backstack, just straight up replaced).

I figure I could just call invalidateOptionsMenu() on the activity whenever the incoming top-level fragment has Resumed, but I feel like there is just something I am missing for it to be handled automatically like when navigating within the top-level fragment.

Twelvemo answered 14/4, 2017 at 2:56 Comment(0)
U
13

The onCreateOptionsMenu method is called after on create or re-create each fragment. What you need to do is clear the menu before inflate the new menu xml. Try to do this:

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState){
    super.onViewCreated(view, savedInstanceState);
    setHasOptionsMenu(true);
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
    super.onCreateOptionsMenu(menu, inflater);
    menu.clear();
    inflater.inflate(R.menu.your_menu, menu);
}
Unmoral answered 19/9, 2017 at 17:57 Comment(4)
What is the reason to use clear() instead of invalidateOptionsMenu() as OP suggests?Argentiferous
@Argentiferous I use clear() to clean the stack menu because if you've another fragment first with another menu items you will have all items.. you need to clear the stack menu. For another hand invalidateOptionsMenu() is deprecated.Unmoral
I think the AppCompat Version is not deprecated. And if you have another fragment first, and replace it, the first menu should not remain if you invalidateArgentiferous
Nevermind, it is deprecated but you can use Activity.invalidateOptionsMenuArgentiferous
C
1

The easiest way is by creating interface in your fragment, and check the fragment visibility by using onAttach/onDetach method:

public class QuickSetup1Fragment extends Fragment {
   private CallbackListener onCallbackListener;
   public QuickSetup1Fragment() {}

   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
       View view = inflater.inflate(R.layout.fragment_quick_setup1, container, false);
       return view;
   }

   @Override
   public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
      super.onViewCreated(view, savedInstanceState);
   }

   public interface CallbackListener{
      public void onAttach(); //fragment is visible
      public void onDetach(); //fragment is invisible/replace/destroy
   }

   @Override
   public void onAttach(Context context) {
       super.onAttach(context);
       try {
           onCallbackListener = (CallbackListener) context;
           onCallbackListener.onAttach();
       } catch (ClassCastException e) {
           throw new ClassCastException(context.toString()
                + " must implement CallbackListener");
       }
   }

   @Override
   public void onDetach() {
       super.onDetach();
       onCallbackListener.onDetach();
       onCallbackListener = null;
   }
}

And implement the interface method on your activity class

public class QuickSetupActivity extends AppCompatActivity implements QuickSetup1Fragment.CallbackListener{

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_image_view);
       Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
       setSupportActionBar(toolbar);
   }

   @Override
   protected void onPostCreate(@Nullable Bundle savedInstanceState) {
       super.onPostCreate(savedInstanceState);
   }

   @OnClick(R.id.backBtn)
   public void back(View v){
       super.onBackPressed();
   }

   @Override
   public void onAttach(){
      //do something with the menu
   }

   @Override
   public void onDetach(){
      //do something with the menu
   }
}

Everytime you change the fragment, onAttach/onDetach will be triggered. You can do specific task here like managing your menus.

Circumstance answered 14/4, 2017 at 4:6 Comment(1)
Thanks, I already have a similar setup with my Top Level fragments which are really just empty fragments that serve as independent backstacks from the activity's own backstack. These fragments interface with the Activity for many lifecycle events and things like back button presses. I could easily use invalidateOptionsMenu() this way, but I am extremely curious why the menu seems automatically managed in the Child Fragment Manager when the Fragment is just hidden, but not in the Activity Frag. Manager when the fragment is completely replaced. I feel like I am missing something.Twelvemo
A
0

Try to implement a callback in your parent fragment that should be called before destroying the child fragment! Inside that call back place oncreateoptions()

Acrefoot answered 14/4, 2017 at 3:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.