Search code examples
javaandroidmenu

Android Studio - MenuProvider in MainActivity and also in Fragments?


I migrated from normal menus to MenuProvider so I added in MainActivity:

private void setMenuProvider() {
    addMenuProvider(new MenuProvider() {
        @Override
        public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {
            menuInflater.inflate(R.menu.menu_action, menu);
        }

        @Override
        public boolean onMenuItemSelected(@NonNull MenuItem menuItem) {
            return false;
        }

        @Override
        public void onPrepareMenu(@NonNull Menu menu) {
            MenuItem itemIcon1 = menu.findItem(R.id.menu_icon1);
            Drawable resIcon1 = getResources().getDrawable(R.drawable.icon_32x32_icon1_green);
            if (statusForIcon1 == false)
                resIcon1.mutate().setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN);
            itemIcon1.setEnabled(statusForIcon1);
            itemIcon1.setIcon(resIcon1);

            MenuItem itemIcon2 = menu.findItem(R.id.menu_icon2);
            Drawable resIcon2 = getResources().getDrawable(R.drawable.icon_32x32_icon2_green);
            if (statusForIcon2 == false)
                resIcon2.mutate().setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN);
            itemIcon2.setEnabled(statusForIcon2);
            itemIcon2.setIcon(resIcon2);
            
            MenuItem itemIcon3 = menu.findItem(R.id.menu_icon3);
            Drawable resIcon3 = getResources().getDrawable(R.drawable.icon_32x32_icon3_green);
            if (statusForIcon3 == false)
                resIcon3.mutate().setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN);
            itemIcon3.setEnabled(statusForIcon3);
            itemIcon3.setIcon(resIcon3);

            MenuProvider.super.onPrepareMenu(menu);
        }
    });
}

and I have two fragment tabs where I added there too:

public void setFragmentOneMenuProvider( LifecycleOwner lcFragmentOne) {
    getActivity().addMenuProvider(new MenuProvider() {
        @Override
        public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {
            menuInflater.inflate(R.menu.menu_action, menu);

            if (myClassObject == null)
                myClassObject = new ClassObject(getActivity(), getContext(), menu);
        }

        @Override
        public boolean onMenuItemSelected(@NonNull MenuItem menuItem) {
            return false;
        }
    }, lcFragmentOne , Lifecycle.State.RESUMED);
}

where I have also :

ublic View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View viewFragmentOne = inflater.inflate(R.layout.fragment_fragmentone, container, true);
    
    setFragmentOneMenuProvider( getViewLifecycleOwner() );
    
    return viewFragmentOne;
}

but in this way with both :

View viewFragmentOne = inflater.inflate(R.layout.fragment_fragmentone, container, true);

and

menuInflater.inflate(R.menu.menu_action, menu);  // this one inside the MenuProvider code

I see like double menus ...does that mean if I add MenuProvider on MainActivity then in fragments then I shall just use:

setHasOptionsMenu(true);

in the way to inform the system that my app bar fragment is participating in the population of the options menu or maybe I have just to modify the MenuProvider in my fragments? In fact , if I click on fab button I added next to refresh status of icons where I call:

getActivity().invalidateMenu();

or

getActivity().invalidateOptionsMenu();

supposing it should refresh the menu inflated by MenuProvider , it doesn't refresh anything....so I tried to comment the fragment's MenuProvider code keeping the inflater.inflate but it didn't changed at all about refreshing when it should refresh...maybe I missed something?...

Thanks in advance to all !! Cheers!!


Solution

  • @Luigino

    I've a similar or even the same requirements as you:

    1. One main menu (defined in R.menu.menu_main and inflated in MainActivity) that contains all required menu entries used in all fragments.
    2. Several fragments whereas I need to execute specific code in each fragment when the user clicks on a particular icon. For example, you're using a menu icon "Save" across all fragments but you want to save data shown in each fragment.

    I've solved it as shown below, i.e. in each fragment in the according onCreateMenu I don't inflate the menu once again. That prevents the menu entries from duplicating.

    See also https://developer.android.com/jetpack/androidx/releases/activity#1.4.0-alpha01 for additional information.

    Hope that helps!

    MainActivity:

    private void setupMenu() {
        MenuProvider menuProvider = new MenuProvider() {
            @Override
            public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {
                menuInflater.inflate(R.menu.menu_main, menu);
            }
    
            @Override
            public boolean onMenuItemSelected(@NonNull MenuItem menuItem) {
                int id = menuItem.getItemId();
                if (id == R.id.menu_action_about) {
                    /* show about dialog */
                    return true;
                } else if (id == R.id.menu_action_settings) {
                    /* show settings dialog */
                    return true;
                } 
                return false;
            }
        };
    
        addMenuProvider(menuProvider);
    }
    

    Fragment 1:

    private void setupMenu() {
        MenuProvider menuProvider = new MenuProvider() {
            @Override
            public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {
                /* do not inflate the menu again */
            }
    
            @Override
            public boolean onMenuItemSelected(@NonNull MenuItem menuItem) {
                int id = menuItem.getItemId();
                if (id == R.id.menu_action_save) {
                    /* execute specific code,
                       e.g. save data shown in fragment one */
                    return true;
                } 
                return false;
            }
        };
    
        requireActivity().addMenuProvider(menuProvider, getViewLifecycleOwner(), Lifecycle.State.RESUMED);
    }