Android Tutorial Material Design 4 - Modifyable and advanced Menu

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

  • ToolBarMenuExample2_0.zip
    18.6 KB · Views: 3,478
Last edited:

miguelconde

Member
Licensed User
Hmm, seems to be that onCreateOptionsMenu is called before Activity_Create. I can't reproduce it here since the oldest Android Version I can test on is 2.3.7 and there I can't see this problem.
Try to change the call to SetCheckboxState in Activity_CreateMenu to an CallSubDelayed() call. This should fix the problem.

Corwin, thanks for replay.

I did it, i made the change, but i new error is generated.

I did this in the CreateMenu:

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)

CallSubDelayed(Menu,"SetCheckBoxState")

End Sub

And this the new error.

LogCat connected to: B4A-Bridge: samsung GT-P1000N
--------- beginning of /dev/log/main
** Activity (main) Pause, UserClosed = false **
** Activity (main) Create, isFirst = false **
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = false **
** Activity (main) Create, isFirst = false **
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = false **
** Activity (main) Create, isFirst = false **
** Activity (main) Resume **
--------- beginning of /dev/log/system
** Activity (main) Pause, UserClosed = false **
** Activity (main) Create, isFirst = false **
** Activity (main) Resume **
hasIcon: false
hasIcon: false
hasIcon: false
hasIcon: false
hasIcon: false
hasIcon: false
** Activity (main) Pause, UserClosed = true **
** Service (starter) Destroy **
Copying updated assets files (1)
** Service (starter) Create **
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = true **
** Service (starter) Destroy **
Installing file.
PackageAdded: package:com.tmcappstore.ce
** Service (starter) Create **
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = true **
** Service (starter) Destroy **
Copying updated assets files (1)
** Service (starter) Create **
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = true **
** Service (starter) Destroy **
Streams_terminated
sending message to waiting queue (CallSubDelayed - UpdateStatus)
Connected to B4A-Bridge (Wifi)
sending message to waiting queue (CallSubDelayed - UpdateStatus)
Installing file.
PackageAdded: package:de.amberhome.appcompat.toolbarmenuexample
Copying updated assets files (2)
** Activity (main) Create, isFirst = true **
Error occurred on line: 54 (Main)
java.lang.RuntimeException: java.lang.NullPointerException
at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayout(LayoutBuilder.java:166)
at anywheresoftware.b4a.objects.PanelWrapper.LoadLayout(PanelWrapper.java:134)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:703)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:340)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:247)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:134)
at de.amberhome.appcompat.toolbarmenuexample.main.afterFirstLayout(main.java:102)
at de.amberhome.appcompat.toolbarmenuexample.main.access$000(main.java:17)
at de.amberhome.appcompat.toolbarmenuexample.main$WaitForLayout.run(main.java:80)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:3687)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at de.amberhome.objects.appcompat.ACCheckBoxWrapper.DesignerCreateView(ACCheckBoxWrapper.java:61)
at anywheresoftware.b4a.objects.CustomViewWrapper.AfterDesignerScript(CustomViewWrapper.java:70)
at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayout(LayoutBuilder.java:158)
... 21 more
** Activity (main) Resume **
 

corwin42

Expert
Licensed User
How can deselect the click option ???
Thanks !!!
Your question seems to be related to the NavigationDrawer of the DesignSupport library. Please move your question to the DesignSupport library thread or open a new thread in the questions forum.
 

yiankos1

Active Member
Licensed User
Hello @corwin42,
I am working on a project using appcompat (and a navigation drawer). App theme is an appcompat theme. I can't figure out why my custom toolbar does not have elevation any more. I think at the very beginning my app had toolbar bar elevetion. Now does not.
p.s.1 setElevation does not work, neither in designer.
Thank you for your time.
 

JohnC

Expert
Licensed User
What version of Android does the device have that doesn't show Elevation?

Android 5+ is needed to show the shadow/elevation.
 

yiankos1

Active Member
Licensed User
What version of Android does the device have that doesn't show Elevation?

Android 5+ is needed to show the shadow/elevation.
Of course my friend, i know basics!
As i tried everything, this one fixed toolbar elevation: "Additionally you should disable the Background color in the properties.".
 

desof

Well-Known Member
Licensed User
Congratulations is a very good material contributed here!
And he was able to run all the examples successfully.
Is there any small example that makes use of Material motion?
 

desof

Well-Known Member
Licensed User
The Tabs ! create un material design here photo un shape wellow.
sorry my english ..
 

corwin42

Expert
Licensed User

AscySoft

Active Member
Licensed User
It is possible to use this type of menu "ACMenu" on any element (item) in CustomListView (lets say a 3 dot button)?
For example, "onclick_event" this material style menu will display some (different) actions, kind of "context menu" in Windows!
 

corwin42

Expert
Licensed User
It is possible to use this type of menu "ACMenu" on any element (item) in CustomListView (lets say a 3 dot button)?
For example, "onclick_event" this material style menu will display some (different) actions, kind of "context menu" in Windows!

Yes, sort of. You can use ACActionMenu of the AppCompat library for this.
Add ACActionMenu to your layout or add it by code (this will create the three dots item). Then use ACActionMenu1.Menu.Add() to add menu items.
 

MichalK73

Active Member
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.


I solved the problem as follows.
I added an empty invisible icon for the ToolBar (Private ToolBar As ACToolBarLight).
Then the function will work 'Activity_ActionBarHomeClick'
For me it looks like this:
B4X:
Sub Activity_Create(FirstTime As Boolean)
Activity.LoadLayout("form_main")
    NavDrawer.Initialize2("NavDrawer", Activity, NavDrawer.DefaultDrawerWidth, NavDrawer.GRAVITY_START)
    NavDrawer.InitDrawerToggle
    ToolBar.InitMenuListener
    Activity.AddMenuItem3("  ","puste",Null,True)
    ToolBar.Color=AC.GetThemeAttribute("colorPrimary")
...
...
...
Now work listening ToolBar
B4X:
Sub puste_Click
    Return
End Sub

Sub Activity_ActionBarHomeClick
    NavDrawer.OpenDrawer
End Sub

If you're not using ToolBar you can get around as follows:
B4X:
Sub Activity_Touch (Action As Int, X As Float, Y As Float)
    If Action=0 Then
        If (X<60dip) And (Y<60dip) Then NavDrawer.OpenDrawer
    End If
End Sub

It works perfectly. :)
 

corwin42

Expert
Licensed User
I solved the problem as follows.
I added an empty invisible icon for the ToolBar (Private ToolBar As ACToolBarLight).
Then the function will work 'Activity_ActionBarHomeClick'
For me it looks like this:
[...]

Looks like a dirty hack.

You should use

B4X:
ToolBar.InitMenuListener

and use the ToolBar_NavigationItemClick event.
 

MichalK73

Active Member
Licensed User
Sorry but it does not work, if ActionMenu is empty.
That's why I had to use dirty hack to create a blank icon (no menu) to 'ToolBar.InitMenuListener' began to work.
 

corwin42

Expert
Licensed User
Sorry but it does not work, if ActionMenu is empty.
That's why I had to use dirty hack to create a blank icon (no menu) to 'ToolBar.InitMenuListener' began to work.
Can you post an example project where it does not work ?
 
Top