Is there a convenient way of showing the same Options menu options in multiple Activities?
Example: In my app, I display a TV Guide in one of three ways.
Seven day guide (TabActivity with 7 tabs)
All channels 'Now showing' (ListActivity)
All shows today by start time (Activity - could be changed easily to ListActivity)
For the Options menu in the TabActivity, the code is quite simple...
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
menu.clear();
inflater.inflate(R.menu.gv_options_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.view:
...
...
}
}
...but at the moment it seems I need to copy/paste it to the other two Activities which I don't like doing. If I change the Options menu code for one I'll need to do it for the other two also.
The only alternative I can think of is I have a 'helper' class (POJO) to which I could add a method and pass the context into to allow use of the getMenuInflator() method and another method I could pass the result of item.getItemId() into to process with the switch-case.
What is the normal way of having multiple Activities with the same Options menu?
Create a simple separate class with these two methods:
public class MyMenuHandler {
private Activity mActivity;
public MyMenuHandler(Activity activity) {
mActivity = activity;
}
public boolean onPrepareOptionsMenu(Menu menu) {
MenuInflater inflater = mActivity.getMenuInflater();
menu.clear();
inflater.inflate(R.menu.gv_options_menu, menu);
return true;
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.view:
...
}
}
}
In your activities override those callback methods and redirect the call to an instance of your MyMenuHandler class:
public class MyActivity1 extends TabActivity {
private MyMenuHandler mMenuHandler;
#Override
public void onCreate(Bundle savedInstanceState) {
...
mMenuHandler = new MyMenuHandler(this);
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
// you may also add here some items which are specific
// for one activity, not for the others
...
return mMenuHandler.onPrepareOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// handle selection of your specific items here,
// if none of them has been selected call mMenuHandler method
...
return mMenuHandler.onOptionsItemSelected(item);
}
}
This will let you hold in one place the code which respond to selection of your basic menu items, so there will be no need to worry about copy-pasting it to all activities which are to have the same menu.
One approach is to use inheritance with your Activities. Create a base Activity that implements the options menu methods and then each child Activity will gain that functionality. This is the recommended approach on the Android developer site:
Tip: If your application contains multiple activities and some of them provide the same Options Menu, consider creating an activity that implements nothing except the onCreateOptionsMenu() and onOptionsItemSelected() methods. Then extend this class for each activity that should share the same Options Menu. This way, you have to manage only one set of code for handling menu actions and each descendant class inherits the menu behaviors.
Unfortunately this won't work for you as you are not inheriting from Activity itself but differing subclasses of it, but that is the 'normal' way to do it.
You can encapsulate your action menu in a fragment. In this way you only need to add the fragment in the onCreate menu of your activity.
You need to call setHasOptionsMenu once the fragment is created.
To add the add fragment use a tag instead of a layout id.
Related
Lets say I have two different activities, both using the same toolbar, making use of the same layout and menu options. This part is no problem.
If I want a menu option to execute a specific function, I would naturally create a function, foo(), and call it when the menu option is selected. Because the onOptionsItemSelected(MenuItem item) method i handled individually in each acitivity, would it be a good practice to use a separate class with static "toolbar functions"? E.g. having a logout() function accessible from the dropdown menu in the toolbar from any activity.
It may seem obvious, however, I cannot find any "best practices" on the matter. So what are the best practice(s) for handling multiple activities calling the same "toolbar function"?
Example code: lets say MainActivity and SecondActivity both have the same toolbar. In both onCreate() methods:
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Somewhere else in both of the acitivites:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.item_foo:
ToolbarFunctions.foo(); //Is this a good way to do it?
default:
break;
}
return super.onOptionsItemSelected(item);
}
Good practice it's using main parent activity class and extend all other activities. Here is some example of your parent Activity:
public abstract class MyParentActivity extends Activity {
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
// This is your abstract function for other activity!
public void foo();
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.item_foo:
foo(); //Is this a good way to do it? Yes, here it's okey!
default:
break;
}
return super.onOptionsItemSelected(item);
}
}
So you can define 10 your activity that extends from MyParentActivity as a code below. That is all:
public class MyOtherActivity extends MyParentActivity {
// Here your function will be called in every time
// when you click to Toolbar buttons!
//
// And you don't need write again about clicking menu!
#Ovveride
public void foo () {
//Any your behaviour
}
}
You can define a BaseActivity class and extend all other activities from the BaseActivity. You would include the common parts in the BaseActivity.
If your foo() function has only logic and no view logic, then is better to keep it in static class.It will be faster and unit testable. If it has view logic in it than go with BaseActivity example. If it has view and functional logic together may be is better to be static and also extend BaseActivity which must implement an interface with a method for your view updates.That interface you will pass to you static foo() method which will do the functional logic and will return with the interface to the activity called it, to let the activity update the view it self. Than you can still unit test your foo() method.
public interface FooInterface{
void onFooDone(Foo foo);
}
public static void foo(FooInterface fooInterface){
calculate...
if(fooInterface!=null)
fooInterface.onFooDone(fooObject);
}
Your activity must implement FooInterface
and call foo(this);
I have 2 xml one main xml and one menu xml. I want to get the click event from menu xml to main xml activity. is there is any way to solve this problem.
Main Home Page
Menu Page
Now click on the menu page button event i want in my main home activity page.
I done Like this
View otherLayout = LayoutInflater.from(this).inflate(R.layout.menu_layout,null);
Button tstclick = (Button) otherLayout.findViewById(R.id.textclick);
tstclick.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Your thing
System.exit(0);
}
});
The Activity class provides us two methods to inflate/show menu items and work with them or assign some tasks to them. They are:
onCreateOptionsMenu(Menu menu)
onOptionsItemSelected(MenuItem item)
The onCreateOptionsMenu() method is responsible for creating and inflating the menu by help of MenuInflater class.
The onOptionsItemSelected() method is responsible for assigning tasks to each menu item. Each menu item is identified by help of its unique ID.
In order to show and work with menu's in any Activity, both the methods needs to be overridden as shown below:
public class MainActivity extends AppCompatActivity {
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
switch (menuItem.getItemId()) {
// All your menu items will come here. Each menu item ID will become a separate case in the Switch case
default:
return super.onOptionsItemSelected(menuItem);
}
}
}
As soon as the user clicks or taps on any menu item, the onOptionsItemSelected() method is called by the Android OS and then the ID for that particular menu item will be matched in the method. The group of statements specified in the respective case will then be executed.
For more info visit the following links:
http://developer.android.com/guide/topics/ui/menus.html
http://developer.android.com/reference/android/view/MenuInflater.html
I'm working with ActionBarSherlock, I need to change some icons on the action bar when the user does some stuff. In order to that I have to save the action bar for later usage :
private Menu actionBarMenu;
...
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getSupportMenuInflater();
inflater.inflate(R.menu.map_activity_menu, menu);
actionBarMenu = menu;
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
actionBarMenu.findItem(R.id.follow_my_position).setIcon(R.drawable.toolbar_myposition_active);
}
Okay ! Here's where the problems begin. When I rotate the screen, I get a NullPointerException on actionBarMenu.
I know... I didn't save it before the screen was rotated, so normally I would go to onSaveInstanceState and save my Menu there, except the actionBarMenu is an interface... More specifically, the interface com.actionbarsherlock.view.Menu and I can't modify it so that it implements Parcelable or Serializable.
So how can I save an interface in the bundle ?
You should not keep reference to your action bar during configuration changes. After screen rotate, Android will recreate your activity and all UI references should be released, otherwise you will introduce reference leak.
Why dont you get your actionBarMenu reference again inside boolean onCreateOptionsMenu, after activity rotates?
You can't. However, you can still retain your member variable on an orientation change by adding the following code to your activity
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Object last = getLastNonConfigurationInstance();
if (last != null && last instanceof Menu) {
actionBarMenu = (Menu) last;
}
}
// for FragmentActivity it is onRetainCustomNonConfigurationInstance
public Object onRetainNonConfigurationInstance() {
return actionBarMenu;
};
You don't. You don't save the interface.
You save some String, or boolean, or integer, representing the action the user did.
Then you check this value and change the menu again.
Does this work before the rotate? Because a rotation just recreates the entire activity - i.e. it goes through the whole creation process again. So, I'm not sure that the recreation is your problem, because onCreateOptionsMenu(Menu menu); should be called again, anyway.
Are you sure that your actionBarMenu.findItem(R.id.follow_my_position) is returning correctly? Instead of handling it how your currently are - why not just...not store the menu and check the clicked menu item instead?
For example:
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch(item.getItemId()){
case R.id.follow_my_position:
item.setIcon(R.drawable.toolbar_myposition_active);
}
}
or if that's not what you're looking for, check that your findItem() call finds the item:
MenuItem item = actionBarMenu.findItem(R.id.follow_my_position);
if(item != null){
item.setIcon(R.drawable.toolbar_myposition_active);
}
The Android design guide says that Help should always be placed as the last item of the overflow menu (it should never appear in the ActionBar) and also, that is should be present in every activity, so that users don't have to look for it. Similar approach is also recommended for Settings.
However, I'm wondering what is the best way to make sure that all the activities in my app handle these items without lots of code repetition? Putting these common items manually into every XML menu file and then manually handling clicks on each of them in every activity class is just nonsense.
Since I am already extending all of my activities from a common base class (which provides some convenience methods), I came up with this approach: In the BaseActivity class, I define an empty initOptionsMenu() method which the subclasses may override (template method pattern style) by adding their specific items to the menu. This method is called at the start of onCreateOptionsMenu() and then the base class adds the common items (settings and help) at the end of the menu.
The onOptionsItemSelected() method follows the standard pattern - it switches on the item ID and in the default case, it passes the handing to the superclass. Again, the base class handles the setting and help cases.
public class BaseActivity extends Activity {
#Override
public boolean onCreateOptionsMenu(Menu menu) {
initOptionsMenu(menu);
menu.add(Menu.NONE, R.id.menu_settings, Menu.NONE, R.string.menu_help);
menu.add(Menu.NONE, R.id.menu_help, Menu.NONE, R.string.menu_help);
return true;
}
protected void initOptionsMenu(Menu menu) {}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_settings:
startActivity(new Intent(this, SettingsActivity.class));
return true;
case R.id.menu_help:
startActivity(new Intent(this, HelpActivity.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
Now, whenever I create a new activity, I extend the BaseActivity. If the derived activity provides some more items for the options menu, I do not override the standard onOptionsItemSelected(), but instead I override the custom initOptionsMenu() method and populate the menu there. In onOptionsItemSelected(), I need to handle only cases for the items specific for this activity, the common ones will be handled by the super call.
public class FooActivity extends BaseActivity {
#Override
protected void initOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.foo, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// cases for items in R.menu.foo
default:
return super.onOptionsItemSelected(item);
}
}
}
Is this pattern sensible? Can you come up with a better approach? Please share your thoughts.
I might not use initOptionsMenu method. I will just call super.onCreateOptionsMenu() after adding my menu from concrete implementation. My BaseActivity will add settings and help menu so
in BaseActivity:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(Menu.NONE, R.id.menu_settings, Menu.NONE, R.string.menu_help);
menu.add(Menu.NONE, R.id.menu_help, Menu.NONE, R.string.menu_help);
return true;
}
and in MainActivity extends BaseActivity:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(Menu.NONE, R.id.menu_create_dummy, Menu.NONE, R.string.menu_dummy);
menu.add(Menu.NONE, R.id.menu_delete_dummy, Menu.NONE, R.string.menu_dummy);
return super.onCreateOptionMenu(menu);
}
I'm an Android newbie, and working on an app which needs an Options menu. I have currently implemented the options menu by setting it as the primary activity and Extending it in the main activity.
But since I work in a team, this method doesn't work always with us since we need to extend another activity which is essential.
My Question
How do I implement this Options Menu across the application without Extending the activity in my Main activity?
My Current Setup
I have a MainActivity (This starts first) - MainActivity extends MenuClass
I have the OptionsMenu Class, MenuClass (I want this to be Application wide) - MenuClass extends Activity
I have three other Classes, that extends Activity itself! And these three activities are triggered from the MainActivity and when done, returns to the MainActivity.
If you don't want to, or can't create a base Activity which every other activity then extends - why don't you have a utilities class which has a public static void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {...} function and a public static boolean onOptionsItemSelected(MenuItem item) {...}?
public class Utils {
public static void onCreateOptionsMenu(Menu menu, MenuInflater inflater ){
//... create default options here
}
public static boolean onOptionsItemSelected(MenuItem item) {
//... see if you want to handle the selected option here, return true if handled
}
}
then from you Activity you can do this:
public class YourActivity extends Activity {
// ...
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater ){
Utils.onOptionsItemSelected(menu, inflater);
//... add other options here
}
public boolean onOptionsItemSelected(MenuItem item) {
boolean handled = Utils.onOptionsItemSelected(item);
if (!handled) {
switch(item.getItemId()) {
case R.id.menu_sign_out:
//... deal with option
break;
//.. deal with other options
}
}
return handled;
}
You may want to change the exact implementation of this depending on how you build it in to your app - ie you may not want the utils methods to be static as you may require some state to be maintained in there, but this should work.