Android Tutorial Material Design 4 - Modifyable and advanced Menu

corwin42

Expert
Licensed User
Note: You should use B4A 6.0 or above for this tutorial.

With the ACToolBar(Light|Dark) object we can get access to the Menu object of the activity. This opens the possibility for modifyable menus and some other advanced menu features.

Basics

With the ToolBar.Menu property we have access to the menu of the ToolBar. For a standalone ToolBar (which is not set as the activity ActionBar) we can just access the property everywhere in our code and do what we want to do with it just like adding/removing menu items.

If the ToolBar is our activities ActionBar we have to handle the menu in a special way because if we don't do this, B4As own menu handling will conflict with our handling.

In Android the menu is created and initalized with two methods: onCreateOptionsMenu() and onPrepareOptionsMenu(). I don't want to explain the whole menu system here you just need to know, that B4A uses these methods internally to create it's own menu and they get called "at some time" in the activity initialization process.

With the itroduction of inline Java code in B4A 4.30 we now have the possibility to add our own code to handle the application menu. The idea is to call an event to set up the menu and here is an example how this can be done:

B4X:
#If Java

public boolean _onCreateOptionsMenu(android.view.Menu menu) {
    if (processBA.subExists("activity_createmenu")) {
        processBA.raiseEvent2(null, true, "activity_createmenu", false, new de.amberhome.objects.appcompat.ACMenuWrapper(menu));
        return true;
    }
    else
        return false;
}
#End If
In this code we use the _onCreateOptionsMenu hook to check if a Sub named "Activity_CreateMenu" exists. If yes, then we call it and we provide the activities menu as a parameter. If the sub does not exist, the normal B4A menu-handling is done.

Now we need the implementation of Activity_CreateMenu:

B4X:
Sub Activity_CreateMenu(Menu As ACMenu)
    Menu.Clear

    Dim item As ACMenuItem
    Menu.Add2(10, 1, "Plus one", xml.GetDrawable("ic_plus_one_black_24dp")).ShowAsAction = item.SHOW_AS_ACTION_IF_ROOM
    Menu.Add2(20, 2, "Refresh", xml.GetDrawable("ic_refresh_black_24dp")).ShowAsAction = item.SHOW_AS_ACTION_ALWAYS

    Menu.Add(1, 3, "Overflow1", Null)
    Menu.Add(2, 4, "Overflow2", Null)
    Menu.Add(3, 5, "Overflow3", Null)

    'Sync Checkboxes with the Menu
    SetCheckBoxState(Menu)

End Sub
First we clear the menu to be sure that there are no old entries in it. Then we just use the Menu object to add some additional menus. Easy, isn't it? (For this example we sync the menu with the checkboxes with SetCheckBoxState())

It is good practice to use a Toolbar as your ActionBar. If you do this you can access the menu with ToolBar.Menu everywhere in your code to modify the menu.

Some advanced menu features

With the ACMenu and ACMenuItem objects we have access to some advanced menu features.

When you add a menu item to the menu you can specify a menu ID and a sort order. So with the sort order you can later add menu entries which are displayed above other menu items. With the ID you can access this item later and modify it.

You can create checkable menu itemes. The ACMenu.Add() methods return the added menu item so you can directly modify it and make it checkable or set it as an action item or even make it invisible.

It is even possible to add or remove menu items dynamically.

B4X:
Sub cbItemVisible_CheckedChange(Checked As Boolean)
  ToolBar.Menu.FindItem(1).Visible = Checked
End Sub

Sub cbItemCheckable_CheckedChange(Checked As Boolean)
  ToolBar.Menu.FindItem(2).Checkable = Checked
End Sub

Sub cbItemDisable_CheckedChange(Checked As Boolean)
  ToolBar.Menu.FindItem(3).Enabled = Not(Checked)
End Sub
Look at the example project for better understanding.

overflow_menu.png
 

Attachments

Last edited:

Peter Simpson

Expert
Licensed User
This is great stuff again @corwin42, thank you. I do have two questions.

1. How can I change the color of the Up Indicator to white from black?
2. Is it possible to capture the Up Indicator click event, I've tried everything that has crossed my mind and I've failed miserably?

Yes I've been busy integrating AppCompat 1.10 into a new app of mine, please look at the screen shot below to see my Up Indicator color issue.
Screenshot_2015-01-08-00-51-50.png



Cheers...
 

corwin42

Expert
Licensed User
This is great stuff again @corwin42, thank you. I do have two questions.

1. How can I change the color of the Up Indicator to white from black?
2. Is it possible to capture the Up Indicator click event, I've tried everything that has crossed my mind and I've failed miserably?
1. Use ToolBarDark instead of ToolBarLight.
2. Activity_ActionBarHomeClick is fired like with the normal ActionBar.
 

Peter Simpson

Expert
Licensed User
1. Use ToolBarDark instead of ToolBarLight.
2. Activity_ActionBarHomeClick is fired like with the normal ActionBar.
Hello @corwin42, thank you for the reply but I've already got those settings.

Here is my current click event
B4X:
'In activity
Sub Activity_ActionBarHomeClick
    CallSub(Null, AnimatePressedState)
End Sub
I've already got these in my code, they just seem not to me working and I'm now baffled...
 

corwin42

Expert
Licensed User
Hmm. Seems to be a problem of the ToolBar that it kills the Activity_ActionBarHomeClick event when there is no menu at all.

Workaround:

Call ToolBar.InitMenuListener and then use the ToolBar_NavigationItemClick event.
 

Peter Simpson

Expert
Licensed User
Hello @corwin42.
This is what happens when I go to the designer and change the theme in the CustomView ActionBar from ACToolBarLight to ToolBarDark, it just dies a sad death...

B4X:
** Activity (main) Create, isFirst = true **
Error occurred on line: 62 (main)
java.lang.RuntimeException: java.lang.RuntimeException: Field actionbar was declared with the wrong type.
    at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayout(LayoutBuilder.java:166)
    at anywheresoftware.b4a.objects.ActivityWrapper.LoadLayout(ActivityWrapper.java:208)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:636)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:305)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:238)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:121)
    at com.simplysoftware.simplyinvoiceae.main.afterFirstLayout(main.java:98)
    at com.simplysoftware.simplyinvoiceae.main.access$100(main.java:16)
    at com.simplysoftware.simplyinvoiceae.main$WaitForLayout.run(main.java:76)
    at android.os.Handler.handleCallback(Handler.java:733)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:146)
    at android.app.ActivityThread.main(ActivityThread.java:5653)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.RuntimeException: Field actionbar was declared with the wrong type.
    at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayoutHelper(LayoutBuilder.java:399)
    at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayoutHelper(LayoutBuilder.java:425)
    at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayout(LayoutBuilder.java:144)
    ... 21 more
 

corwin42

Expert
Licensed User
Hello @corwin42.
This is what happens when I go to the designer and change the theme in the CustomView ActionBar from ACToolBarLight to ToolBarDark, it just dies a sad death...
You have to change the type of your global variable for the ToolBar, too.
 

Peter Simpson

Expert
Licensed User
Your work around works perfect @corwin42.
Workaround:
Call ToolBar.InitMenuListener and then use the ToolBar_NavigationItemClick event.
Hmmm.
You have to change the type of your global variable for the ToolBar, too.
Dam it, I'm a bit annoyed with myself with that one. It's not as if I've not seen it constantly for the last couple of days :mad:

Thank you...
 
Last edited:

dbalman

Member
Licensed User
Is it possible to add an icon to overflow menu items? Neither specifing an icon as android.graphics.drawable.Drawable or as an android.graphics.Bitmap seem to work.

B4X:
ActionBar.Menu.Add2(1, 3, "Overflow1", xml.GetDrawable("ic_plus_one_black_24dp"))
ActionBar.Menu.Add(2, 4, "Overflow2", LoadBitmap(File.DirAssets, "csrc.png"))
Thanks
 

corwin42

Expert
Licensed User
Great Stuff Corwin.. I love This..
But,How to Disable ContextMenu On Edittext
If you just want to let the contextual ActionBar overlap the ToolBar, just add the following line to the theme file:
B4X:
<item name="windowActionModeOverlay">true</item>
If you want to disable the contextual ActionBar completely use this:
After loading the layout or initializing the EditText:
B4X:
Dim jo As JavaObject = EditText1
Dim e As Object = jo.CreateEvent("android.view.ActionMode.Callback", "Action", False)
jo.RunMethod("setCustomSelectionActionModeCallback", Array As Object(e))
And add the following Event sub:
B4X:
Sub Action_Event (MethodName As String, Args() As Object) As Object
    Return False
End Sub
 

nadhiras

Member
Licensed User
If you just want to let the contextual ActionBar overlap the ToolBar, just add the following line to the theme file:
B4X:
<item name="windowActionModeOverlay">true</item>
If you want to disable the contextual ActionBar completely use this:
After loading the layout or initializing the EditText:
B4X:
Dim jo As JavaObject = EditText1
Dim e As Object = jo.CreateEvent("android.view.ActionMode.Callback", "Action", False)
jo.RunMethod("setCustomSelectionActionModeCallback", Array As Object(e))
And add the following Event sub:
B4X:
Sub Action_Event (MethodName As String, Args() As Object) As Object
    Return False
End Sub

Thanks Corwin..
it's Work
 

FireDroid

Member
Licensed User
If you just want to let the contextual ActionBar overlap the ToolBar, just add the following line to the theme file:
B4X:
<item name="windowActionModeOverlay">true</item>
If you want to disable the contextual ActionBar completely use this:
After loading the layout or initializing the EditText:
B4X:
Dim jo As JavaObject = EditText1
Dim e As Object = jo.CreateEvent("android.view.ActionMode.Callback", "Action", False)
jo.RunMethod("setCustomSelectionActionModeCallback", Array As Object(e))
And add the following Event sub:
B4X:
Sub Action_Event (MethodName As String, Args() As Object) As Object
    Return False
End Sub
Very useful !
Great tip thanks ;)
 

nadhiras

Member
Licensed User
hello again corwin
how to animate three line menu icon (burger) into arrow icon (up indicator) when drawer open
 

lemonisdead

Well-Known Member
Licensed User
Hello All,

Excuse me but do you get the NavigationItemClick event ? I have tried with
Activity_NavigationItemClick, ActionBar_NavigationItemClick, Activity_ActionBarHomeClick but got nothing in return.

Many thanks
 

johndb

Active Member
Licensed User
Hello All,

Excuse me but do you get the NavigationItemClick event ? I have tried with
Activity_NavigationItemClick, ActionBar_NavigationItemClick, Activity_ActionBarHomeClick but got nothing in return.

Many thanks
ToolBar_MenuItemClick (Item As ACMenuItem)
 

RandomCoder

Well-Known Member
Licensed User
Hello All,

Excuse me but do you get the NavigationItemClick event ? I have tried with
Activity_NavigationItemClick, ActionBar_NavigationItemClick, Activity_ActionBarHomeClick but got nothing in return.

Many thanks
It took a few attempts but I've found that this works (I seem to recall that the Activity standard ActionBar didn't fire the event unless the UpIndicator was displayed)...

B4X:
... ' Toolbar initialisation
    ABHelper.Initialize ' ABHelper was dimmed as ACActionBar
    ABHelper.ShowUpIndicator = True ' Need to set to True!
... ' Any other code in initialisation sub

Private Sub Activity_ActionBarHomeClick
    ToastMessageShow("CLICKED", True)
End Sub
 
Top