Android Toolbar and Menu are driving me crazy - android

I've made a Toolbar in the Activity xml file (snipped for brevity)
<Toolbar
android:id="#+id/mytoolbar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:theme="#style/ThemeOverlay.AppCompat.Dark"
android:title="#string/password_manager"
android:logo="#android:drawable/ic_menu_view"
android:background="#color/colorPrimary"/>
I did try other flavors of toolbar - androidx.appcompat..., ...appbar.MaterialToolbar, and the one I came across the most often android.support.v7.widget.Toolbar. These crash my app.
I made a menu xml that has this (snipped for brevity):
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/hmenu_createnew"
android:checkable="false"
android:enabled="true"
android:title="#string/create_new"
app:showAsAction="always" />
</menu>
At this point everything looked and worked fine. Now - I want to dynamically control the menu items. I added the override:
#Override
public boolean onPrepareOptionsMenu (Menu menu) {
super.onPrepareOptionsMenu( menu );
this.menu = menu;
return true;
}
And here's where it's gotten frustrating. The above override was not called. I did a ton of searching and came across one person who suggested a change to the Activity class declaration.
He said instead of 'Extends Activity' change to 'Extends AppCompatActivity'
I checked my class declaration and it already was AppCompat. On a whim I changed it to Activity.
With this change I can see now in debugger my override is getting called. Yeay!
But my menu is nowhere to be found!.
In my activity.class are these few lines (snipped for brevity):
protected void onCreate(Bundle savedInstanceState) {
toolbar = (Toolbar) findViewById(R.id.mytoolbar);
setActionBar( toolbar );
toolbar.inflateMenu( R.menu.home_mainmenu );
}
Since that no longer worked I removed toolbar.inflateMenu(), replaced it with getMenuInflater() and put it in the override, as below.
#Override
public boolean onCreateOptionsMenu (Menu menu) {
super.onCreateOptionsMenu( menu );
getMenuInflater().inflate( R.menu.home_mainmenu, menu );
return false;
}
No joy.
I also tried this:
#Override
public boolean onPrepareOptionsMenu (Menu menu) {
super.onPrepareOptionsMenu( menu );
getMenuInflater().inflate( R.menu.home_mainmenu, menu );
this.menu = menu;
return false;
}
Still no joy.
In the two overrides above I also tried substituting getMenuInflater.... for toolbar.inflateMenu...
No joy.
What I want is a regular toolbar with a right justified standard button with the three vertical dots that displays a menu when clicked.
At this point I am clueless so let me close by saying THANK YOU in advance to the angel out there who has the solution.
Does anyone else think this is needlessly difficult?..... Again, TIA.

I suggest applying androidX migration. So Toolbar package will be
androidx.appcompat.widget.Toolbar and for parent activity androidx.appcompat.app.AppCompatActivity
Make sure that the Activity theme is not inherited from Theme.AppCompat.NoActionBar
use setSupportActionBar(toolbar) method not setActionBar in onCreate or inside overridden setContentView method
Inflate custom menu before super.onCreateOptionsMenu(menu):
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(/*menu res id*/)
this.menu = menu;
return super.onCreateOptionsMenu(menu)
}
With such setup it should work

Well I eventually did figure it out. To repeat a bit, I am using this library:
import android.widget.Toolbar;
because I want to use Activity - not AppCompatActivity
so the xml declaration looks like this: <Toolbar ..... />
Because we are using Toolbar we must remove the default ActionBar that Android gives us. This requires an edit of the app manifest.
Anywhere you find "AppTheme.ActionBar" change to "AppTheme.NoActionBar"
in the java class I have this:
protected void onCreate(Bundle savedInstanceState) {
Toolbar toolbar = findViewById(R.id.mytoolbar);
setActionBar( toolbar );
}
Note the use of 'setActionBar' as opposed to 'setSupportActionBar'. That is correct.
Then there is the (finally!) properly coded override which might be informative to anyone regardless of what import library you choose:
#Override
public boolean onPrepareOptionsMenu (Menu menu) {
super.onPrepareOptionsMenu( menu );
if ( !bMenuInitialized ) {
this.menu = menu;
getMenuInflater().inflate( R.menu.home_mainmenu, menu );
bMenuInitialized = true;
}
/*
invalidateOptionsMenu();
toolbar.inflateMenu( R.menu.home_mainmenu );
*/
return true;
}
I left in the commented block what NOT to do. Calling invalidateOptionsMenu(), which you will see all over the internet, causes an infinite loop. Do not do that..... Only call invalidateOptionsMenu from some other code block if you have altered the menu in some way. This is the reason why the toolbar was not showing itself. (By 'altering the menu' I don't mean setting attributes on MenuItems.)
I also learned that you MUST create a flag to indicate if the menu has been inflated yet, because the onPrepareOptionsMenu() gets called every time the user accesses the toolbar. You don't want to keep inflating your menu every time because it just adds to the menu that's already in it making it longer and longer.
There you have it. I hope this helps someone. And thank you to all who responded.

Related

Do I need an empty onCreateOptionsMenu for empty menus?

In my app I have some activities without menu items, that use the following override:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.appbar_menu_empty, menu);
return true;
}
This works good. If I remove the override, I get the same effect on Android 5.1, i.e. an action bar with no icons.
So the question is: can I drop the override?
The documentation of Activity.onCreateOptionsMenu states:
The default implementation populates the menu with standard system menu items.
What does that mean? Do I need to expect that Android comes up with some buttons I did not explicitly add?
You can remove OncreateOptionsMenu() if you dont want to have menu items.
If you want to add menu items, edit the menu.xml file in resources/menu directory.
From the docs the method is defined in Activity class as below
Initialize the contents of the Activity's standard options menu. You
should place your menu items in to menu.
This is only called once, the first time the options menu is
displayed. To update the menu every time it is displayed, see
onPrepareOptionsMenu(android.view.Menu).
The default implementation populates the menu with standard system
menu items. These are placed in the android.view.Menu.CATEGORY_SYSTEM
group so that they will be correctly ordered with application-defined
menu items. Deriving classes should always call through to the base
implementation.
You can safely hold on to menu (and any items created from it), making
modifications to it as desired, until the next time
onCreateOptionsMenu() is called.
When you add items to the menu, you can implement the Activity's
onOptionsItemSelected(android.view.MenuItem) method to handle them
there.
Parameters:
menu The options menu in which you place your items. Returns:
You must return true for the menu to be displayed; if you return false it will not be shown.
public boolean onCreateOptionsMenu(Menu menu) {
if (mParent != null) {
return mParent.onCreateOptionsMenu(menu);
}
return true;
}
also check this SO thread out onCreateOptionsMenu() calling super
check the code for Activity class here http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/app/Activity.java#Activity.onCreateOptionsMenu%28android.view.Menu%29
see some sample code here where you need to show option in action bar menu
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_act_add_recipe, menu);
super.onCreateOptionsMenu(menu, inflater);
}
/res/menu/menu_act_add_recipe.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_add_image"
android:icon="#drawable/ic_tab_add_image_white"
android:orderInCategory="100"
android:title="#string/action_preview"
app:showAsAction="always" />
<item
android:id="#+id/action_recipe_preview"
android:icon="#drawable/ic_tab_check_white"
android:orderInCategory="100"
android:title="#string/action_preview"
app:showAsAction="always" />

Android - Correct use of invalidateOptionsMenu()

I have been searching a lot on invalidateOptionsMenu() and I know what it does. But I cannot think of any real life example where this method could be useful.
I mean, for instance, let's say we want to add a new MenuItem to our ActionBar, we can simply get the Menu from onCreateOptionsMenu(Menu menu) and use it in any button's action.
Now to my real question, is following the only way of using invalidateOptionsMenu()?
bool _OtherMenu;
protected override void OnCreate (Bundle bundle)
{
_OtherMenu = false;
base.OnCreate (bundle);
SetContentView (Resource.Layout.Main);
Button button = FindViewById<Button> (Resource.Id.myButton);
button.Click += delegate
{
if(_OtherMenu)
_OtherMenu = false;
else
_OtherMenu = true;
InvalidateOptionsMenu ();
};
}
public override bool OnCreateOptionsMenu (IMenu menu)
{
var inflater = this.SupportMenuInflater;
if(_OtherMenu)
inflater.Inflate (Resource.Menu.another_menu, menu);
else
inflater.Inflate (Resource.Menu.main_activity_menu, menu);
return base.OnCreateOptionsMenu (menu);
}
Click the button and a different menu appears. Click the button again and previous menu appears.
P.S. Sorry for the C# syntax.
invalidateOptionsMenu() is used to say Android, that contents of menu have changed, and menu should be redrawn. For example, you click a button which adds another menu item at runtime, or hides menu items group. In this case you should call invalidateOptionsMenu(), so that the system could redraw it on UI. This method is a signal for OS to call onPrepareOptionsMenu(), where you implement necessary menu manipulations.
Furthermore, OnCreateOptionsMenu() is called only once during activity (fragment) creation, thus runtime menu changes cannot be handled by this method.
All can be found in documentation:
After the system calls onCreateOptionsMenu(), it retains an instance
of the Menu you populate and will not call onCreateOptionsMenu() again
unless the menu is invalidated for some reason. However, you should
use onCreateOptionsMenu() only to create the initial menu state and
not to make changes during the activity lifecycle.
If you want to modify the options menu based on events that occur
during the activity lifecycle, you can do so in the
onPrepareOptionsMenu() method. This method passes you the Menu object
as it currently exists so you can modify it, such as add, remove, or
disable items. (Fragments also provide an onPrepareOptionsMenu()
callback.)
On Android 2.3.x and lower, the system calls onPrepareOptionsMenu()
each time the user opens the options menu (presses the Menu button).
On Android 3.0 and higher, the options menu is considered to always be
open when menu items are presented in the action bar. When an event
occurs and you want to perform a menu update, you must call
invalidateOptionsMenu() to request that the system call
onPrepareOptionsMenu().
use this to reload new menu during app lifecycle:
new:
getActivity().invalidateOptionsMenu();
old
ActivityCompat.invalidateOptionsMenu(getActivity());
You need to override method onPrepareOptionsMenu(), write your update code of action menu in same method and if you are using fragment then add setHasOptionsMenu(true); in onCreateView().
Hope it helps you
One use I've found is forcing an order of operations between onResume and onCreateOptionsMenu/onPrepareOptionsMenu. The natural order (as of platform 22 at least) seems to flip flop around, especially when re-orientating your device.
Call invalidateOptionsMenu() in onResume() and you'll guarantee that onPrepareOptionsMenu will be called after onResume (it may additionally be called before). For example, this will allow enabling a menu item based on data retrieved in onResume.
/**
* Set a hint for whether this fragment's menu should be visible. This
* is useful if you know that a fragment has been placed in your view
* hierarchy so that the user can not currently seen it, so any menu items
* it has should also not be shown.
*
* #param menuVisible The default is true, meaning the fragment's menu will
* be shown as usual. If false, the user will not see the menu.
*/
public void setMenuVisibility(boolean menuVisible) {
if (mMenuVisible != menuVisible) {
mMenuVisible = menuVisible;
if (mHasMenu && isAdded() && !isHidden()) {
mHost.onSupportInvalidateOptionsMenu();
}
}
}
XML menu sample:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_edit"
android:icon="#drawable/ic_edit"
android:title="Edit Task"
app:showAsAction="always" />
<item
android:id="#+id/action_delete"
android:icon="#drawable/ic_delete"
android:title="Delete Task"
app:showAsAction="always" />
<item
android:id="#+id/action_check"
android:icon="#drawable/ic_check"
android:title="Check Task"
app:showAsAction="always" />
<item
android:id="#+id/action_uncheck"
android:icon="#drawable/ic_undo"
android:title="Uncheck Task"
app:showAsAction="always" />
</menu>
Code inside a sample fragment:
private boolean isMenuItemChecked;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setMenuVisibility(false);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.my_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
try {
menu.findItem(R.id.action_check).setVisible(!isMenuItemChecked);
menu.findItem(R.id.action_uncheck).setVisible(isMenuItemChecked);
}
catch(Exception e) {
Log.e(TAG, "onPrepareOptionsMenu error");
}
}
public void loadUi(boolean isMenuItemChecked) {
this.isMenuItemChecked = isMenuItemChecked;
setMenuVisibility(true);
}
Put the initial state of the menu in onCreateOptionsMenu(...).
Use the invalidateOptionsMenu() to force onCreateOptionsMenu(...) and onPrepareOptionsMenu(...).
In onPrepareOptionsMenu(...), call menu.clear() to remove all items from the menu.
Still in onPrepareOptionsMenu(...) place your dynamic menu changes after the clear.
Edit: Here is a better answer to the question.
A good use for invalidateOptionsMenu() is when we have a ListView and Delete All MenuItem so when the ListView is empty we should use invalidateOptionsMenu() to remove the Delete All MenuItem.
Here is a question related to this answer: Question.
It's old, but hope this helps some one out in the future.
One use I found on real life scenario:
Assume you've a list of items that are stored into database, and you've 2 activities:
DisplayActivity: which displayed these objects after getting them
from database.
EditActivity: used to edit an existing item & save that into database.
You decided to have a couple of options to go from DisplayActivity to EditActivity:
First: To add a brand-new item into database.
Second: To edit an existing item.
In order not to repeat yourself by duplicating code, you decided to use EditActivity for both purposes.
And so, you want to customize Options Menu according to each purpose. For this case you'd build a default options menu using onCreateOptionsMenu(), and leave it as-is when it's time to edit an existing item; and invalidateOptionsMenu() it when it's time to create new items; and in this case onPrepareOptionsMenu() is auto triggered for customizing your menu.
For instance the Options menu can have a delete option for editing an existing item, and this should be hidden when adding a new item.
From fragment call getActivity().invalidateOptionsMenu();.

How to set action bar and left drawer attributes for fragments according to their types

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);
}

How to use the TabActivity Menu without interference with the children tabs

This topic can look similar to others but I haven't found any usable answer for this case.
Here is what I want : I have a TabActivity with a menu, containing tabs without any menu. When I press the menu button, I want the only existing menu to be displayed. If I only inflate the menu, this is working fine. But if I want to change the content of the menu (change the visibility of items in the onPrepareOptionsMenu(menu) method) or even press the items of the menu, nothing works.
For the onPrepareOptionsMenu(menu), the problem seems to be coming from the menu.findItem(...) method which returns a null Object, and I have a NullPointerException !
In onOptionsItemSelected(item), none of the item IDs will be recognized.
I have noticed with the debugger that the menu Context is the activity of the current tab, so I have moved the menu inside of that activity instead, but without more success.
Last thing, I was using the same menu and very similar code in a previous version of the app, using a single activity (no tabs) and I didn't encounter any problem. When I moved to the TabActivity design it was first working fine (maybe the context of the menu was my TabActivity instead of the Activity of the tab), but it didn't work anymore after minor changes to the Activities of the tabs (nothing related to any menu).
If you think using ActionBar (and the support package) would fix this, I am already planning in moving to it later, but I would like to understand and fix this first.
Here is the code :
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
getMenuInflater().inflate(R.menu.main_menu, menu);
return true;
}
#Override
public boolean onPrepareOptionsMenu(Menu menu)
{
MenuItem connect1 = menu.findItem(R.id.connect_1);
MenuItem connect2 = menu.findItem(R.id.connect_2);
// Here I do some stuff to prepare the menu, which could be simplified
// like this :
if (device1Connected)
if (!connect1.isVisible()) // here I get the NullPointerException
connect1.setVisible(true);
return super.onPrepareOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
// Handle item selection
switch (item.getItemId()) {
case R.id.select_device:
// do some stuff
return true;
case R.id.connect_1:
// do some stuff
return true;
case R.id.connect_2:
// do some stuff
return true;
case R.id.disconnect:
// do some stuff
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Here is the XML file of the menu, nothing special
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/select_device"
android:icon="#drawable/icon"
android:title="#string/choose_devices"/>
<item android:id="#+id/connect_1"
android:icon="#android:drawable/ic_menu_add"
android:title="#string/connect_1" />
<item android:id="#+id/connect_2"
android:icon="#android:drawable/ic_menu_add"
android:title="#string/connect_2" />
<item android:id="#+id/disconnect"
android:icon="#android:drawable/ic_menu_close_clear_cancel"
android:title="#string/disconnect" />
</menu>
Edit : I have added the following in the onPrepareOptionsMenu(menu) to avoid the NullPointerException. It doesn't fix the real problem but I can now see precisely what happens when I click on a MenuItem
if (connect1 == null || connect2 == null)
return super.onPrepareOptionsMenu(menu);
When I click on the first item, getItemId() returns, for example, 2131165207, and the second item returns 2131165208 (I have checked, they are ids of Views of the second tab !!), but the values it should return to enter the switch/case are respectively 2131165215 and 2131165216, so as I said before, I have a problem with my item ids. I made this test with the menu within the Activity of the first tab, because the mContext value of the menu is always an instance of the current Activity. But even though the Context AND the Activity containing the menu are the same, it still doesn't work.
So ! I have finally figured out what was wrong. It was a building/compiling error. I have been trying before refreshing the project many time, but it was not enough, I had also tried deleting the R.java file (where are compiling the ID numbers from the XML files), but it still didn't work; and I finally remembered about the Project -> Clear... function in Eclipse while working on another project.
And it just worked. So that was nothing there very challenging, but at least I've learnt something valuable today.

How to add my menu button in system bar in Android 3.1?

My pad is SAMSUNG GT-P7510.I want to add a new menu in the system bar.But the menu shows in action bar.Like this:
But now it is like this:
It's in the right of top.
public class TestActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0,0,1,"OK");
return true;
}
}
my guess is you started off with a pre-honeycomb app and ran it on 3.*+ emulator.
it happens automatically if you use the right layout style/theme.
on eclipse just go to your layout and choose "android 3.0" on the top right corner.
hope it helps.
EDIT:
after you edited your question I understand i got your question wrong.
If I understand correctly you are just trying to show the menu item as a button outside the menu list and that's simple -
on the xml use the "showAsAction" option like so-
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/menu_share"
android:icon="#drawable/ic_menu_share"
android:title="#string/menu_share"
android:alphabeticShortcut='o'
android:showAsAction="ifRoom|withText" />
</menu>
inflating it with
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.list_options_menu, menu);
or by code:
MenuItem item = menu.add("OK");
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
Please remove targetSdkVersion from manifest.xml file..
like from this format:
make it like:
Thanks,
Ram

Categories

Resources