I see that it's possible to handle a tap on a icon menù item or by implementing
onOptionsItemSelected
inside the acivity, or by using
onMenuItemClickListener
like onclick listener on a button. When is better to use the fist one method, and when the second one?
Because for my opinion, using an external listener makes more modular the code, but create a new class, but using the first way don't create new class, but makes code less modular...
There are use cases other than the ones outlined below, but I'm putting in the general cases that come up regularly.
onOptionsItemSelected
If you're using Fragments, you may want to use onOptionsItemSelected and consider adding menu items to the Action Bar the way that is described in Adding items to the Action Bar.
What this describes is implementing onCreateOptionsMenu inside your Fragment. To make this happen, you must call setHasOptionsMenu in onCreate.
protected void onCreate(Bundle savedInstanceState) {
this.setHasOptionsMenu(true);
}
Setting this will actually make the Activity call onCreateOptionsMenu which allows you to add the menu items.
#Override
public boolean onCreateOptionsMenu(Menu menu){
super.onCreateOptionsMenu(menu);
// add items corresponding to this Fragment
menu.add(...);
return true;
}
The reason I recommend this is that it allows you to put more of the menu handling code into your Fragment instead of the Activity to figure out which Fragment to call, etc.
In this case, clicking the menu item will call onOptionsItemSelected inside of your Fragment which I suggest.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.my_id1:
dothing1();
return true;
case R.id.my_id2:
dotghing2();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
More of a long winded answer, but this is the way to handle menu clicks inside your Fragment.
onMenuItemClickListener
In the case of onMenuItemClickListener, this is used when you DON'T want to use the pre-ready method above and implement your own.
What I mean by that is you implement OnMenuItemClickListener and generate the methods in the interface. You then assign the menu to call the Activity that implemented this where as the above option assumes what Activity to use based on the pre-ready implementation of the Activity to Fragment relationship.
If you are targeting API 14 or greater (ICS or above) you could implement an ActionProvider. If that's not an option then you could implement a base activity that will always populate the menu and handle any menu clicks using onOptionsItemSelected. This is a good approach to implement "About" or "Settings" menu items through all your activities.
Related
I am having Viewpager with 3 fragments. I want to show menu in only one of the fragments.
1st, I don't know why toolbar.inflateMenu doesn't work.
2nd, the menu works, if I have onPrepareOptionsMenu method and do
getMenuInflater().inflate(R.menu.add_user, menu); but the menu is displayed in all the fragments.
So, according to other questions in stack overflow, I implemented the below code, but it doesn't show the menu, it enters the method but menu is not shown.
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
//getMenuInflater().inflate(R.menu.add_user, menu);
info("Menu=>${tabs_viewpager.currentItem}")
val menuItem = menu?.findItem(R.id.addUserMenu)
menuItem?.setVisible(tabs_viewpager.currentItem == 1)
return true
}
The control comes to this method and shows currentItem. But it doesn't display the menu. Menu is there with id. Can someone direct me what can I correct to get this work?
try calling setHasOptionsMenu(true); in oncreate() of the fragments where you want the menu to show (you can set it to false in the fragments where you do not want the menu to show).
also include:
#Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
// your code
}
in the fragments where you want to set/change the menu. this is called everytime before the menu is shown.
you can also call invalidateOptionsMenu() or supportInvalidateOptionsMenu() (if you're using the support library) to force onPrepareOptionsMenu(menu) to be called.
you may want to check out this menus tutorial
good luck
clive
I have a Toolbar being used as an ActionBar with two items. I only want to ever display one at a time as they kind of replace each other. The problem is that when i replace a Fragment, it call onCreateOptionsMenu and will inflate the menu again, meaning that the same action button will be shown, even if the other one was previously in the ActionBar. I have to need to change anything in the ActionBar from my Fragments or when a new Fragment is displayed(with FragmentManager.FragmentTransaction.replace()). So my question is how do I not call onCreateOptionsMenu when a new fragment is displayed?
I can't use a boolean because I will still need it to reinflate on orientation change. And any advice on how to handle orentation change for my situation?
I can post code, but it seems more conceptual and I'm not sure that it would help.
I solved the problem by instead of not calling onCreateOptionsMenu, I added the items to my menu manually.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
boolean refreshVisible;
if (refreshItem != null && refreshItem.isVisible()){//is being displayed prior to inflation
refreshVisible = true;
}else if (refreshItem == null){//it's null so the menu has never been created
refreshVisible = true;
}else {//it's not null and invisibe, other icon was being displayed
refreshVisible = false;
}
menu.clear();//clear menu so there are no duplicate or overlapping icons
getMenuInflater().inflate(R.menu.main, menu);//inflate menu
refreshItem = menu.findItem(R.id.refresh);
useDataItem = menu.findItem(R.id.use_data);
refreshItem.setVisible(refreshVisible);//if menu is being created for first time or item was previously visible, then display this item
useDataItem.setVisible(!refreshVisible);//display this item if not displaying other
return true;
}
I would fiddle with the onPrepareOptionsMenu hook. If you can detect that your menu should not be shown you should jest return false from there. Per documentation:
Prepare the Screen's standard options menu to be displayed. This is called right before the menu is shown, every time it is shown. You can use this method to efficiently enable/disable items or otherwise dynamically modify the contents.
and
You must return true for the menu to be displayed; if you return false it will not be shown.
You can call setHasOptionsMenu(false); inside your fragment.
This will prevent onCreateOptionsMenu() from being called when that fragment added.
I have various types of fragment in my application and there are 3 icons on ActionBar (filter, refresh and sort) but I don't want to show all 3 icons in each of the fragments. I have to show only some of them according to the fragment.
Similar thing I want to do with left drawer. On some fragments I want to show left drawer whereas don't want to display left drawer on others.
I have a Activity class in my application on which I am attaching these fragments and currently I am handling these two things in this class and code has become mess with if-else conditions.
So right now I am checking fragment name and then setting action bar icons and left drawer attributes according to it.
Please tell me a better way to do it( preferably to handle this in Fragment itself)
Thanks
Fragments have access to their activity through getActivity() function which will return non-null activity after onAttach() is called (and before onDetach()). Once the fragment has the activity it can tell it to do whatever you were doing right in the activity manually with checks, including changing the action bar buttons.
In order to show the options depending on the fragment, you can simply do the following:
Add setHasOptionsMenu(true) to the onCreate() method of the fragment and tell the Activity to redraw its options menu.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
getActivity().invalidateOptionsMenu();
}
Next override the onCreateOptionsMenu() method to inflate the options that you want for your fragment.
// No support library - support library api slightly different
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Add Fragment menu elements to Activity menu elements
inflater.inflate(R.menu.myfragmentmenu, menu);
super.onCreateOptionsMenu(menu,inflater);
}
Finally make sure to capture all option items in the onOptionsItemSelected() method of your activity.
(Important note: make sure to replace fragments instead of adding them. Otherwise onCreateOptionsMenu() will be called for each fragment.)
In order to disable and enable the drawer, you can add the following method to your Activity and call it from your fragment:
public void toggleDrawer(boolean enabled) {
if (enabled) {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
mDrawerToggle.setDrawerIndicatorEnabled(true);
} else {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
mDrawerToggle.setDrawerIndicatorEnabled(false);
}
}
Create a new project from a sample project called ActionBarCombat and also download this sample application here http://www.learn2crack.com/2014/06/android-sliding-navigation-drawer-example.html
I once combined the two to come up with an application has action bar attributes as well as left drawer
To structure your code a bit why don't you create some methods in your activity like displayRefreshIcon(boolean visible) in which you handle the visibly of these items.
From your fragment you can call these methods (like frangulyan suggests) through the getActivity() function.
If (getActivity() != null && getActivity() instanceof MyActivity) {
((MyActivity)getActivity()).displayRefreshIcon(true);
}
In main thread or usual it is somehow impossible to do make changes in the activity itself, because fragments are separated module who are attached with activities but not the part of them.
But there is a shortcut that is to send message (handler) to activity to update the show the respective actionbar components
(most probably if you are using this fragment only for specific activity).
There you should make a base fragment and each fragment should extend baseFragment and at onResume method you have to check instance of Fragment then according to them you can update actionBar View.
Using any type of fragment, you should just have access to the methods (you have to override them): onCreateOptionsMenu, onPrepareOptionsMenu and onOptionsItemSelected. These methods should provide you with plenty of handles to create a menu per fragment. You could create a menu layout file per fragment and handle them in the method designed to do so. The methods:
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.overviewmenu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
}
I'm practising adding menu items and trying to react to menu item clicks. According to the developer's guide, it says:
Tip: Android 3.0 adds the ability for you to define the on-click behavior for a menu item in XML, using the android:onClick attribute. The value for the attribute must be the name of a method defined by the activity using the menu. The method must be public and accept a single MenuItem parameter—when the system calls this method, it passes the menu item selected. For more information and an example, see the Menu Resource document.
However, the sample code in the same page doesn't follow the rule: the methods do not pass the MenuItem parameter. The sample code is:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.new_game:
newGame();
return true;
case R.id.help:
showHelp();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
My question is: Shouldn't method calls be newGame(MenuItem item) and showHelp(MenuItem item), instead of newGame() and showHelp()? When I tested my own, (MenuItem item) argument was needed in fact, otherwise, the app was crashing, even though it compiles correctly.
Any help would be appreciated.
onOptionsItemSelected is the alternative to defining onClick attributes and what is available prior to Android 3.0 (important if you want to be backward compatible). It is simply a different way of providing the same process flow. Of course, onClick has the potential to crash your application on runtime, rather than onOptionsItemSelected not handling a menu item (simply causing it to do nothing).
When you create a button, you can assign a listener to capture user actions like clicks.
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//do some stuff
}
});
}
My question is why menu items don't need a listener? All I need is just write
getMenuInflater().inflate(R.menu.main, menu);
inside of
public boolean onCreateOptionsMenu(Menu menu).
and use
public boolean onOptionsItemSelected(MenuItem item)
to define behaviour when an Item is clicked. I didn't see any setListener method. Besides, the main activity class does not implement any Listener. How does a menuItem work?
How does a menuItem work?
So OptionsMenu is build-in widget in Android OS
It works simply said like when you'll click on an item then is immediately called "OnItemSelectedListener" (you don't need to implement it, it's automatic called whenever you'll click on the item). This listener is already implemented. You don't have to implement it and i think you shouldn't and can't.
An onOptionsItemSelected method is called by listener that is called when you'll click on the item.
And what docs exactly say:
This hook is called whenever an item in your options menu is selected.
The default implementation simply returns false to have the normal
processing happen (calling the item's Runnable or sending a message to
its Handler as appropriate). You can use this method for any items for
which you would like to do processing without those other facilities.
Derived classes should call through to the base class for it to
perform the default menu handling.